developers
ES Sign up

Quickstart

The heysteff API

Send messages over WhatsApp, Instagram, Messenger, SMS and Gmail, sync contacts, trigger flows and get every reply on your server through signed webhooks. One REST API, one API key, JSON.

Base
https://app.heysteff.com/api/v1
Auth
Authorization: Bearer
Format
JSON
Spec
OpenAPI 3.1

In three steps you will send a WhatsApp message from your code and receive the customer reply on your server.

Create an API key#

No account yet? Create a developer account: the first screen creates your API key and leaves this example ready to copy.

If you already have a workspace, open Settings → Developers → API Keys and click New API key. Name it and pick the scopes your integration needs; for this guide, messages:write and conversations:read are enough.

The secret (it starts with sk_live_) is shown only once. Store it in an environment variable:

terminal
export HEYSTEFF_API_KEY="sk_live_..."
Server side only

An API key grants access to your workspace. Never ship it in browser code, a mobile app or a repository. If it leaks, revoke it from the same screen.

Send your first message#

You need a connected channel in the workspace (WhatsApp, for example). Without channel_connection_id, the message goes out through the workspace default channel for the API.

curl https://app.heysteff.com/api/v1/messages \
  -H "Authorization: Bearer $HEYSTEFF_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: order-1042-shipped" \
  -d '{
    "channel": "whatsapp_cloud",
    "to": "+15551234567",
    "content": "Hi Ana, your order #1042 is on its way."
  }'

The response returns the queued message. Delivery to the provider happens right away, in the background:

201 Created
{
  "data": {
    "id": "0b6e1c2a-5f3d-4e8a-9c1b-7d2e3f4a5b6c",
    "conversation_id": "9a8b7c6d-5e4f-4a3b-8c2d-1e0f9a8b7c6d",
    "status": "pending",
    "content": "Hi Ana, your order #1042 is on its way.",
    "content_type": "text",
    "created_at": "2026-10-31T14:02:11.482Z"
  }
}

The Idempotency-Key header is optional here, but worth sending: if your code retries after a network error, the customer still gets the message once. More in Errors and limits.

Outside the 24-hour window#

WhatsApp only allows free-form text within 24 hours of the customer last message. Outside that window, use a template approved by Meta:

curl
curl https://app.heysteff.com/api/v1/messages \
  -H "Authorization: Bearer $HEYSTEFF_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "channel": "whatsapp_cloud",
    "to": "+15551234567",
    "content_type": "template",
    "template": {
      "name": "order_shipped",
      "language": "en_US",
      "body_params": ["Ana", "#1042"]
    }
  }'

Over SMS#

With a heysteff SMS number connected to the workspace, the same endpoint sends SMS. Use "channel": "sms" and the phone in international format:

curl
curl https://app.heysteff.com/api/v1/messages \
  -H "Authorization: Bearer $HEYSTEFF_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: order-1042-sms" \
  -d '{
    "channel": "sms",
    "to": "+56912345678",
    "content": "Your order #1042 is on its way. Reply STOP to opt out."
  }'
  • Text only: content_type must be text, up to 1,600 characters. A phone that is not in international format, another content type or a longer text return 422 invalid_sms_message.
  • If you pass a channel_connection_id, it must belong to an SMS number; otherwise the response is 422 sms_sender_mismatch.
  • Billing is per segment: 160 characters without accents or emojis (153 per part when the message is split), or 70 if it has any (67 per part). Each message allows up to 3 segments and the workspace 1,000 a day; a message over the limit fails and is not charged.
  • Anyone who replies STOP, BAJA or CANCEL stops getting SMS from your number. Replies reach your webhook with "channel_type": "sms".

Receive replies with a webhook#

In Settings → Developers → Outbound webhooks, create a webhook with your server URL and the message:received event. Copy the secret it shows you: you will use it to verify every delivery.

Whenever a customer writes, heysteff POSTs this body to your URL:

POST your-server/webhooks/heysteff
{
  "id": "5d1f0c7e-8a2b-4c3d-9e4f-0a1b2c3d4e5f",
  "tenant_id": "2c7e9b1a-3d4f-4a5b-8c6d-7e8f9a0b1c2d",
  "event": "message:received",
  "ts": "2026-10-31T14:03:52.104Z",
  "data": {
    "conversation_id": "9a8b7c6d-5e4f-4a3b-8c2d-1e0f9a8b7c6d",
    "message_id": "4e3d2c1b-0a9f-4e8d-7c6b-5a4f3e2d1c0b",
    "channel_type": "whatsapp_cloud",
    "content": "Thanks! What time will it arrive?",
    "content_type": "text",
    "contact_id": "7f6e5d4c-3b2a-4190-8f7e-6d5c4b3a2918",
    "media_url": null,
    "filename": null
  }
}

Before trusting the content, verify the X-Heysteff-Signature header. The Webhooks guide has Node.js and Python code plus a test vector to check your implementation.

Start from a template#

If you would rather start from a working project, create one with a single command. It copies the template, puts your key in .env.local (taken from the CLI if you already ran heysteff login) and installs the dependencies.

Terminal
npm create heysteff@latest

The templates are open source (MIT): change them however you like.

Next steps#

Coding with an AI agent?#

Claude Code, Cursor or Codex work better with the API as plain text. Give your agent these links:

The four steps, a ready prompt for your agent and the commands to connect the MCP server are in Coding agents and MCP.