# Integrations

Connect Ogave SMS to the tools you already use. Everything goes through the same engine as the API: your plan prices, your balance, your sender IDs, blacklist and opt-outs. Manage connections in the portal under **Integrations**.

| Platform | What it does | How it connects |
| --- | --- | --- |
| [Zapier](https://app.ogave.co/docs/integrations/zapier) · [Make](https://app.ogave.co/docs/integrations/make) | Instant triggers: message delivered, link clicked, inbound message, contact opted out. Actions: send SMS, add / update contact. | Your API key (REST hooks) |
| [Shopify](https://app.ogave.co/docs/integrations/shopify) | Abandoned cart, order created, order shipped (tracking), OTP codes | OAuth app install |
| [WooCommerce](https://app.ogave.co/docs/integrations/woocommerce) | Abandoned checkout, order created, order completed (tracking), OTP codes | REST API keys or a webhook secret |
| [Tiendanube / Nuvemshop](https://app.ogave.co/docs/integrations/tiendanube) | Abandoned cart, order created / paid / shipped | Partner app install (OAuth) |
| [VTEX](https://app.ogave.co/docs/integrations/vtex) | Order created / paid / invoiced, abandoned carts from a trigger | App key + app token |
| [PrestaShop](https://app.ogave.co/docs/integrations/prestashop) | Abandoned cart, order created / paid / shipped | Webservice key (read every few minutes) |
| [Magento 2 / Adobe Commerce](https://app.ogave.co/docs/integrations/magento) | Abandoned cart, order created / paid / shipped | Integration access token (read every few minutes) |
| [Jumpseller](https://app.ogave.co/docs/integrations/jumpseller) | Abandoned checkout, order created / paid / shipped | API login + token, hooks token |
| [HubSpot](https://app.ogave.co/docs/integrations/hubspot) | Two-way contact sync (on demand + scheduled), SMS logged on the contact timeline | OAuth |
| [Zoho CRM](https://app.ogave.co/docs/integrations/zoho) | Contacts or leads both ways, opt-outs, SMS and replies as notes, send SMS from workflow rules | OAuth |
| [Google Sheets](https://app.ogave.co/docs/integrations/google-sheets) | Keep a contact list in sync with a sheet | Public link, or OAuth for private sheets |
| [Okta](https://app.ogave.co/docs/integrations/okta) | Okta's SMS codes (MFA, phone enrollment) delivered with your Verify app | Telephony inline hook + API key (verify) |
| [Auth0](https://app.ogave.co/docs/integrations/auth0) | Auth0's SMS codes (MFA, enrollment) delivered with your Verify app | Custom phone provider Action + API key (verify) |

## REST hooks {#hooks}

Subscribe a URL to one event. Events are delivered by our webhook system: signed (`Webhook-Signature`, same scheme as [API webhooks](https://app.ogave.co/docs/api#webhooks)), retried with backoff, listed in `GET /v1/webhooks/deliveries`. A hook belongs to the mode of the key that created it: test keys get test events only. Answer `410 Gone` to delete the subscription.

| POST | `/v1/hooks` | `{"event": "message.delivered", "target_url": "https://…", "platform": "zapier"}` → `201` `{"id": "hook_…", …}` |
| --- | --- | --- |
| GET | `/v1/hooks` | Your subscriptions (mode of the key) |
| DELETE | `/v1/hooks/{id}` | Unsubscribe |
| GET | `/v1/hooks/samples/{event}` | Your last 3 real events of that type, or one example (for field mapping) |

```
curl -X POST https://app.ogave.co/v1/hooks -H "Authorization: Bearer $API_KEY" -H "Content-Type: application/json" \
  -d '{"event":"message.inbound","target_url":"https://hooks.example.com/sms"}'
```

## Events {#events}

Every delivery has the same envelope: `{"id": "evt_…", "type": "<event>", "mode": "live", "created_at": "…", "data": {…}}`.

| Event | When | data |
| --- | --- | --- |
| `message.delivered` | An SMS from the API, the portal or a campaign is delivered | The message object (`source: "portal"` for portal traffic) |
| `message.clicked` | A tracked short link is clicked | Message + `url`, `short_url`, `clicked_at`, `device`, `os`, `browser` |
| `message.inbound` | A reply / MO to one of your numbers | `from`, `to`, `text`, `channel`, `received_at` |
| `contact.opted_out` | Unsubscribe from a list (keyword, STOP, link, form) or a number added to the blacklist | `phone`, `scope` (`group` \| `all`), `group_id`, `group_name`, `source`, `keyword`, `opted_out_at` |

```
{"id":"evt_01jb…","type":"contact.opted_out","mode":"live","created_at":"2026-10-31T10:15:00Z",
 "data":{"object":"opt_out","phone":"+573001234567","scope":"group","group_id":"6ae4daa666119","group_name":"Newsletter","source":"keyword","keyword":"baja","opted_out_at":"2026-10-31T10:15:00Z"}}
```

> STOP produces one `scope: "all"` event (blacklist) plus one `scope: "group"` event per list the number was in.

## Contacts {#contacts}

| GET | `/v1/contact-lists` | Your lists: `id`, `name`, `contacts` |
| --- | --- | --- |
| POST | `/v1/contact-lists` | `{"name": "Leads"}` |
| POST | `/v1/contacts` | Add or update (the phone is the key inside a list). `201` created, `200` updated. |
| GET | `/v1/contacts?list_id=&phone=` | Search (up to 100) |
| GET · PATCH · DELETE | `/v1/contacts/{id}` | One contact |

```
curl -X POST https://app.ogave.co/v1/contacts -H "Authorization: Bearer $API_KEY" -H "Content-Type: application/json" \
  -d '{"list_id":"6ae4daa666119","phone":"+573001234567","first_name":"Ana","email":"ana@example.com","fields":{"COMPANY":"Acme"}}'
```

Rules (same as file imports): the number must be valid (international, or national with `country`); blacklisted numbers are refused (`422 opted_out`); a contact that unsubscribed is updated but never re-subscribed; `fields` keys are the field tags of the list and values must fit the field type (date `YYYY-MM-DD`, number, URL…), else `422` with `param: "fields.<TAG>"`.
