# 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 three 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. **Connect it to Stream.** Run the calls in [Set it up from your backend](#set-it-up-from-your-backend).

Sync the agent:

```sh
curl -fsSL https://getstream.io/cli/beta/install.sh | bash # the agents commands are in the beta CLI
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.

## 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 [Set it up from your backend](#set-it-up-from-your-backend) 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.

## Set it up from your backend

Save your Meta app and create the connection. `$STREAM_ACCELERATION_URL` is your router's URL, and `$SERVER_TOKEN` is a [server token](https://getstream.io/agents/docs/php/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`.

Then point Meta at Stream. Do it after the calls above: 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}`.

### 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"}}}'
```

## 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

- [Connectors](https://getstream.io/agents/docs/php/connectors/): Whose account, the catalog, and the keys of a connector entry.
- [SMS](https://getstream.io/agents/docs/php/connectors/sms/): Answer over SMS on your own Telnyx number.
- [iMessage](https://getstream.io/agents/docs/php/connectors/imessage/): Answer over iMessage on your own Linq line.
- [Slack](https://getstream.io/agents/docs/php/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/php/connectors/whatsapp/](https://getstream.io/agents/docs/php/connectors/whatsapp/).