# Quickstart

Make your first authenticated request, connect a customer and send a template message.

> **In a nutshell:** Register your customer as a business, send their admin to the connect link, then send an approved template with a reference. Everything else arrives as callbacks.

## Before you start

You need a **product token** from a PaalChat operator. It is shown once - put it
in your server's secret store, for example `PAALCHAT_TOKEN`.

> [!WARNING]
> Never use the token in a browser, mobile app or public repository. All calls to
> PaalChat must come from your server.

## 1. Check the token

```bash
curl https://whatsapp.paaltech.org/api/v1/me \
  -H "Authorization: Bearer $PAALCHAT_TOKEN" \
  -H "Accept: application/json"
```

```php
$me = Http::withToken(config('services.paalchat.token'))
    ->acceptJson()
    ->get('https://whatsapp.paaltech.org/api/v1/me')
    ->throw()
    ->json('data');
```

```javascript
const res = await fetch('https://whatsapp.paaltech.org/api/v1/me', {
  headers: { Authorization: `Bearer ${process.env.PAALCHAT_TOKEN}`, Accept: 'application/json' },
});
const { data: me } = await res.json();
```

```python
import os, requests

me = requests.get(
    "https://whatsapp.paaltech.org/api/v1/me",
    headers={"Authorization": f"Bearer {os.environ['PAALCHAT_TOKEN']}", "Accept": "application/json"},
).json()["data"]
```

The response names your product and the token's abilities:

```json
{"data": {"product": "multi-skuul", "name": "Multi-SKUUL",
  "token": {"name": "production", "abilities": ["businesses.read", "businesses.write", "connections.read", "connections.manage", "templates.read", "messages.read", "messages.send"], "expires_at": null}}}
```

## 2. Register a customer as a business

Use your own ID for the customer as `external_id`. Calling it again updates the business.

```bash
curl -X POST https://whatsapp.paaltech.org/api/v1/businesses \
  -H "Authorization: Bearer $PAALCHAT_TOKEN" -H "Accept: application/json" -H "Content-Type: application/json" \
  -d '{"external_id": "presec", "name": "Presec Legon", "email": "info@presec.edu.gh"}'
```

```php
Http::withToken(config('services.paalchat.token'))->acceptJson()
    ->post('https://whatsapp.paaltech.org/api/v1/businesses', [
        'external_id' => 'presec',
        'name' => 'Presec Legon',
        'email' => 'info@presec.edu.gh',
    ])->throw();
```

```javascript
await fetch('https://whatsapp.paaltech.org/api/v1/businesses', {
  method: 'POST',
  headers: { Authorization: `Bearer ${process.env.PAALCHAT_TOKEN}`, Accept: 'application/json', 'Content-Type': 'application/json' },
  body: JSON.stringify({ external_id: 'presec', name: 'Presec Legon', email: 'info@presec.edu.gh' }),
});
```

```python
requests.post(
    "https://whatsapp.paaltech.org/api/v1/businesses",
    headers={"Authorization": f"Bearer {TOKEN}", "Accept": "application/json"},
    json={"external_id": "presec", "name": "Presec Legon", "email": "info@presec.edu.gh"},
).raise_for_status()
```

## 3. Connect their WhatsApp

Ask for a one-time link and redirect the customer's administrator to it.

```bash
curl -X POST https://whatsapp.paaltech.org/api/v1/businesses/presec/whatsapp/connect \
  -H "Authorization: Bearer $PAALCHAT_TOKEN" -H "Accept: application/json" -H "Content-Type: application/json" \
  -d '{"return_url": "https://presec.skuuls.paaltech.org/settings/whatsapp"}'
```

```php
$link = Http::withToken(config('services.paalchat.token'))->acceptJson()
    ->post('https://whatsapp.paaltech.org/api/v1/businesses/presec/whatsapp/connect', [
        'return_url' => route('settings.whatsapp'),
    ])->throw()->json('data');

return redirect()->away($link['url']);
```

```javascript
const { data: link } = await (await fetch(`${BASE}/businesses/presec/whatsapp/connect`, {
  method: 'POST', headers: HEADERS,
  body: JSON.stringify({ return_url: 'https://presec.skuuls.paaltech.org/settings/whatsapp' }),
})).json();
res.redirect(link.url);
```

```python
link = requests.post(f"{BASE}/businesses/presec/whatsapp/connect", headers=HEADERS,
    json={"return_url": "https://presec.skuuls.paaltech.org/settings/whatsapp"}).json()["data"]
return redirect(link["url"])
```

The admin logs in to Meta on PaalChat's page and picks a number. You receive a
`whatsapp.connection` callback with `"event": "connected"`. See [Connect WhatsApp](/docs/connect-whatsapp).

## 4. Send a template

Business-initiated messages must use an approved template. Always send your own `reference`.

```bash
curl -X POST https://whatsapp.paaltech.org/api/v1/businesses/presec/messages \
  -H "Authorization: Bearer $PAALCHAT_TOKEN" -H "Accept: application/json" -H "Content-Type: application/json" \
  -d '{
    "to": "+233241234567",
    "type": "template",
    "reference": "skuul-msg-8812",
    "template": {"name": "fees_reminder", "language": "en_US",
      "components": [{"type": "body", "parameters": [
        {"type": "text", "text": "Mrs Mensah"}, {"type": "text", "text": "Kofi"},
        {"type": "text", "text": "450.00"}, {"type": "text", "text": "30 October"}]}]}
  }'
```

```php
$message = Http::withToken(config('services.paalchat.token'))->acceptJson()
    ->post('https://whatsapp.paaltech.org/api/v1/businesses/presec/messages', [
        'to' => '+233241234567',
        'type' => 'template',
        'reference' => 'skuul-msg-'.$notification->id,
        'template' => [
            'name' => 'fees_reminder',
            'language' => 'en_US',
            'components' => [['type' => 'body', 'parameters' => [
                ['type' => 'text', 'text' => 'Mrs Mensah'], ['type' => 'text', 'text' => 'Kofi'],
                ['type' => 'text', 'text' => '450.00'], ['type' => 'text', 'text' => '30 October'],
            ]]],
        ],
    ])->throw()->json('data');
```

```javascript
const { data: message } = await (await fetch(`${BASE}/businesses/presec/messages`, {
  method: 'POST', headers: HEADERS,
  body: JSON.stringify({
    to: '+233241234567', type: 'template', reference: `skuul-msg-${notification.id}`,
    template: { name: 'fees_reminder', language: 'en_US', components: [{ type: 'body', parameters: [
      { type: 'text', text: 'Mrs Mensah' }, { type: 'text', text: 'Kofi' },
      { type: 'text', text: '450.00' }, { type: 'text', text: '30 October' }] }] },
  }),
})).json();
```

```python
message = requests.post(f"{BASE}/businesses/presec/messages", headers=HEADERS, json={
    "to": "+233241234567", "type": "template", "reference": f"skuul-msg-{notification.id}",
    "template": {"name": "fees_reminder", "language": "en_US", "components": [{"type": "body", "parameters": [
        {"type": "text", "text": "Mrs Mensah"}, {"type": "text", "text": "Kofi"},
        {"type": "text", "text": "450.00"}, {"type": "text", "text": "30 October"}]}]},
}).json()["data"]
```

`202 Accepted` means the message is queued. `200 OK` means that `reference` was
already sent - you get the original message back and nothing is sent twice.

## 5. Receive callbacks

A PaalChat operator registers your callback URL and gives you a signing secret.
You will receive `message.status` as the message is sent, delivered and read, and
`message.received` when the parent replies. Verify every callback - see [Callbacks](/docs/callbacks).

## Next steps

- [Sending messages](/docs/sending-messages) in depth, including free-form text and the 24-hour window.
- [Receiving messages](/docs/receiving-messages) to build an inbox.
- [Safe retries](/docs/safe-retries) so network failures never send twice.
