xYago APIv1

Messages

Send service notifications (order shipped, appointment confirmed, payslip available...) to your customers and employees through the xYago app chat, from a verified business profile. Delivery is through the xYago network only: if the person does not have the app yet, the message waits for them for up to 7 days.

Before sending

  1. In the panel → API Developer → Senders, create your sender (its id, e.g. tiendaexpress, display name, logo, website and category) and submit it for review. xYago verifies and approves it.
  2. Create a template for each kind of notification, with its texts in es, en and/or de and numbered variables {{1}}, {{2}}... Submit it for review: xYago approves it. Only service notifications (category utility) are allowed, never advertising. No links in the text: to link, add a button (up to 3 quick reply or link buttons; see Templates and buttons).
  3. Enable the Messages product and create an API key with the messages:write scope (and messages:read to query).

Use GET /senders and GET /templates to check the review status of your senders and templates through the API.

Send a message

curl -X POST 'https://api.xyago.com/v1/messages' \
  -H "Authorization: Bearer $XYAGO_API_KEY" \
  -H "Idempotency-Key: pedido-TE-48213-enviado" \
  -H "Content-Type: application/json" \
  -d '{"to": "+573005550123", "sender_id": "tiendaexpress", "template": "pedido_enviado",
       "language": "en", "variables": ["Valentina", "TE-48213"], "reference": "CLI-000123"}'

202 response:

{
  "id": "7a1c9e35-2b4d-4f6a-8c0e-1d3f5a7b9c21",
  "status": "sent",
  "to_masked": "+57 ••• ••• 0123",
  "sender_id": "tiendaexpress",
  "template": "pedido_enviado",
  "language": "es",
  "reference": "CLI-000123",
  "failure_code": null,
  "created_at": "2026-11-04T15:00:00Z",
  "sent_at": "2026-11-04T15:00:01Z",
  "delivered_at": null,
  "read_at": null,
  "expires_at": null
}

Statuses

StatusMeaning
acceptedReceived by the API (transient).
pending_installThe phone does not use xYago yet: the message waits up to 7 days (expires_at).
sentStored in the recipient's chat.
deliveredReached one of the recipient's devices.
readThe recipient opened the chat.
failedWill not be delivered. failure_code: recipient_not_on_xyago (did not install xYago within 7 days), undeliverable, sender_suspended, invalid_message.

Query a message with GET /messages/{id} or list yours with GET /messages?status=&sender_id=&reference=; better, subscribe to the webhooks.

Recipients without xYago (pending_install)

If the phone does not use xYago yet, the API does not fail: it answers 202 with status: "pending_install". xYago keeps the message encrypted and delivers it as soon as that person installs the app and verifies their number (you get message.sent). If they do not within 7 days the message ends as failed with recipient_not_on_xyago (you get message.failed) and the text is deleted. To speed this up, invite your users.

If the recipient cannot receive messages from your sender the API answers 409 undeliverable. Do not retry that message.

Idempotency-Key (required)

Every POST /messages carries the Idempotency-Key header (8-128 characters A-Z a-z 0-9 . _ : -), unique per message: the notification id in your system or a UUID. If the response is lost (timeout, 502, 503), repeat the request with the same key and the same body:

RepetitionResponse
Same key, same body200 with the original message and the Idempotent-Replayed: true header (or the same error if it had failed). Never sent twice.
Same key, different body409 idempotency_key_reused.

Keys are per environment: the same key in sandbox and live are different messages.

Limits

Besides the API key's per-minute request limit, each sender has caps against spam: messages per minute (the product limit in the panel), messages per recipient per day (10) and new recipients per day (1,000). Above them the API answers 429 rate_limited with Retry-After. If xYago detects that many messages go to phones without xYago, it pauses the sender's messages to new recipients (403 new_recipients_paused) until it reviews it with you.

If the limits cannot be checked at that moment nothing is sent, for safety: 503 limits_unavailable. Retry with the same Idempotency-Key.

Webhooks

Subscribe in the panel to message.sent, message.delivered, message.read, message.failed, message.button_clicked (button tapped), sender.status_changed and template.status_changed (see Webhooks). Its data is the same object returned by GET /messages/{id}:

{
  "created_at": "2026-03-02T15:00:04Z",
  "data": {
    "id": "3e5a7c91-4b6d-4e8f-a0c2-5d7f9b1e3a64",
    "status": "sent",
    "to_masked": "+57 ••• ••• 0187",
    "sender_id": "tiendaexpress",
    "template": "pedido_enviado",
    "language": "es",
    "reference": "CLI-000087",
    "failure_code": null,
    "created_at": "2026-11-04T15:00:00Z",
    "sent_at": "2026-11-05T08:10:00Z",
    "delivered_at": null,
    "read_at": null,
    "expires_at": "2026-11-11T15:00:00Z"
  },
  "environment": "live",
  "id": "evt_4f9c2b7a1d3e4c5b8a6f0e1d2c3b4a59",
  "type": "message.sent"
}

Sandbox

With an xy_test_ key messages are real (they reach the app), but only to your test numbers: up to 5, which must use xYago. Add them in the panel → Sandbox; each one receives in xYago a 6-digit code from xYago Sandbox that you type in the panel to confirm it. In sandbox you can use any sender of your account and approved or draft templates; while your sender is not approved, messages arrive from xYago Sandbox prefixed with your sender's display name, e.g. [Sandbox · Tienda Express] Hi Valentina, …. Sending to another number answers 422 sandbox_recipient_not_allowed.