# WhatsApp

Many of your customers already use WhatsApp. For instance, a customer sends your business number "What is your return policy?", and the agent answers in the same WhatsApp chat. The `whatsapp` connector does this with your own Meta app.

## Answer on WhatsApp

Add the `whatsapp` connector to your agent:

```yaml title="agents/support/agent.yaml"
name: support
mode: text
llm: llm-fast
connectors:
  - name: whatsapp
    connection: acme-whatsapp # the label of your WhatsApp connection
```

You need five things from Meta: your app's ID and App Secret, your number's phone number ID, its WhatsApp account ID, and an access token. We recommend starting with Meta's test number. Setup takes six steps:

1. **Create a Meta app**

   At [developers.facebook.com](https://developers.facebook.com/apps), create an app with the use case **Connect with customers through WhatsApp**. The App ID and App Secret are under **App settings > Basic**. Under **Use cases > Customize > Step 1. Try it out**, copy the test number's **Phone Number ID** and **Messaging account ID**, and add your own phone as a **Recipient**.

2. **Create a token that doesn't expire**

   At [business.facebook.com](https://business.facebook.com), open **Settings > Users > System users** and add an **Admin** system user. Give it full control of your app and your WhatsApp account, then choose **Generate token** with **Never** as the expiration and the `whatsapp_business_messaging` and `whatsapp_business_management` permissions.

3. **Save your Meta app**

   Give Stream the App ID and the App Secret:

   ```sh
   curl -fsSL https://getstream.io/cli/beta/install.sh | bash # the agents commands are in the beta CLI
   echo "$META_APP_SECRET" | getstream agents connectors client set whatsapp --provider-app-id "$META_APP_ID" --signing-secret -
   ```

   The CLI prints `Saved the provider app for whatsapp`.

4. **Connect the number**

   Create the connection with the phone number ID and the access token:

   ```sh
   echo "$WHATSAPP_ACCESS_TOKEN" | getstream agents connections create whatsapp --label acme-whatsapp --input phone_number_id=$PHONE_NUMBER_ID --token -
   ```

   The CLI prints `Connected: whatsapp / acme-whatsapp`, with the connection's id.

5. **Point Meta at Stream**

   >
   > **Warning:** Do it after steps 3 and 4: Meta checks the URL at once.
   >

   The verify token is your Meta app ID, and `$WABA_ID` is the Messaging account ID:

   ```bash
   curl -X POST "https://graph.facebook.com/v25.0/$META_APP_ID/subscriptions" \
     --data-urlencode "access_token=$META_APP_ID|$META_APP_SECRET" \
     --data-urlencode "object=whatsapp_business_account" \
     --data-urlencode "callback_url=$STREAM_ACCELERATION_URL/v1/connectors/events/whatsapp/$META_APP_ID" \
     --data-urlencode "verify_token=$META_APP_ID" \
     --data-urlencode "fields=messages"

   curl -X POST "https://graph.facebook.com/v25.0/$WABA_ID/subscribed_apps" \
     -H "Authorization: Bearer $WHATSAPP_ACCESS_TOKEN"
   ```

   Both answer `{"success": true}`.

6. **Sync the agent**

   ```sh
   getstream agents sync support
   ```

You can't start a chat with a test number from your phone, so [send yourself Meta's `hello_world`](#say-hello-from-a-test-number) first. Reply "What is your return policy?" in that chat, and the agent's answer comes back in the same chat.

### In the Dashboard

Steps 3 and 4 work in the Dashboard too. In the [Stream Dashboard](https://getstream.io/signin/), go to **Agents > Library > Connectors** and open **WhatsApp**.

1. Click **Set OAuth client**. The dialog has two fields: fill **Provider app ID** with your Meta app ID and **Signing secret** with the App Secret, then click **Save client**.
2. Click **Create connection**. **Owner** starts on **Signed-in user**: pick **App**. Type `acme-whatsapp` in **Label**, the phone number ID in **phone_number_id** and the access token in **Token**, then click **Create connection**. The connection's page opens, and the connection is connected.

### Say hello from a test number

Put your own phone in `to`:

```bash
curl -X POST "https://graph.facebook.com/v25.0/$PHONE_NUMBER_ID/messages" \
  -H "Authorization: Bearer $WHATSAPP_ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"messaging_product": "whatsapp", "to": "+12025550100", "type": "template", "template": {"name": "hello_world", "language": {"code": "en_US"}}}'
```

## Send a photo

A photo reaches the agent. Send a photo with "What is in this photo?", and the agent answers about the photo. A photo with no caption works too.

If photos do not reach the agent, save the token again. Run the third call in [With the API](#with-the-api) once more with your access token. Set `expected_revision` to the `revision` that `GET $STREAM_ACCELERATION_URL/v1/agents/connections/$CONNECTION_ID` returns.

## Keywords

Stream handles these, not the agent:

- HELP: Stream answers "Reply STOP to unsubscribe."
- STOP: the person is opted out. Stream answers "You are unsubscribed and will receive no more messages. Reply START to resubscribe.", and the agent answers nothing more from them until they send START.
- START: the person is opted back in. Stream answers "You are subscribed again. Reply STOP to unsubscribe."

## Limitations

The agent replies only within [24 hours](https://developers.facebook.com/docs/whatsapp/cloud-api/guides/send-messages#customer-service-windows) of the person's last message.

## With the API

Your server can save your Meta app and create the connection instead of the CLI. `$STREAM_ACCELERATION_URL` is your router's URL, and `$SERVER_TOKEN` is a [server token](https://getstream.io/agents/docs/auth/):

```bash
curl -X PUT "$STREAM_ACCELERATION_URL/v1/agents/connectors/whatsapp/oauth-client" \
  -H "api_key: $STREAM_API_KEY" -H "stream-auth-type: jwt" \
  -H "Authorization: Bearer $SERVER_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"provider_app_id": "<Meta app ID>", "signing_secret": "<App Secret>"}'

curl -X POST "$STREAM_ACCELERATION_URL/v1/agents/connections" \
  -H "api_key: $STREAM_API_KEY" -H "stream-auth-type: jwt" \
  -H "Authorization: Bearer $SERVER_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"connector_id": "whatsapp", "owner": {"type": "app"}, "label": "acme-whatsapp", "inputs": {"phone_number_id": "<phone number ID>"}}'

curl -X PUT "$STREAM_ACCELERATION_URL/v1/agents/connections/$CONNECTION_ID/credentials" \
  -H "api_key: $STREAM_API_KEY" -H "stream-auth-type: jwt" \
  -H "Authorization: Bearer $SERVER_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"expected_revision": 1, "values": {"token": "<access token>"}}'
```

`$CONNECTION_ID` is the `id` the second call returns. After the third call, the connection is `connected`.

## When a message gets no answer

| What you see                                      | Why                                             | What to do                                                   |
| ------------------------------------------------- | ----------------------------------------------- | ------------------------------------------------------------ |
| WhatsApp says your test number is not on WhatsApp | A test number can't be found in a search        | [Send yourself `hello_world`](#say-hello-from-a-test-number) |
| Meta answers `(#133010) Account not registered`   | The number isn't registered with Meta's API yet | Register it, as below                                        |
| A person gets no reply after STOP                 | The person opted out                            | The person sends START                                       |

To register a number, pick a six-digit PIN and keep it. It becomes the number's two-step verification PIN:

```bash
curl -X POST "https://graph.facebook.com/v25.0/$PHONE_NUMBER_ID/register" \
  -H "Authorization: Bearer $WHATSAPP_ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"messaging_product": "whatsapp", "pin": "<six digits>"}'
```

## Going further

- [Channels](https://getstream.io/agents/docs/connectors/channels/): Every channel, what it needs, and which one to start with.
- [SMS](https://getstream.io/agents/docs/connectors/sms/): Answer over SMS on your own Telnyx number.
- [iMessage](https://getstream.io/agents/docs/connectors/imessage/): Answer over iMessage on your own Linq line.
- [Slack](https://getstream.io/agents/docs/connectors/slack/): Answer in Slack threads as your workspace's bot.

---

For the most recent version of this documentation, visit [https://getstream.io/agents/docs/connectors/whatsapp/](https://getstream.io/agents/docs/connectors/whatsapp/).