xYago APIv1

Templates and buttons

Templates are the notifications you can send with POST /messages: texts per language with variables, up to 3 buttons and a review by xYago before they can be used in production. You create them in the panel (API Developer → Templates); the API only reads them (GET /templates).

Text

Buttons

A template can have up to 3 buttons, shown below the message in the same order. Like the body, the button text is per language (texts, with the same shape as languages: e.g. {"es": "Seguir pedido", "en": "Track order"}): one for each language of the template, max. 20 characters, no variables and different from the other buttons. Each message shows the buttons in the same language as the body.

TypeFieldsWhat happens when tapped
quick_replytexts, payload (optional, max. 128, fixed in the template)You receive the message.button_clicked webhook with your payload. It is not a message: the user types nothing and the payload never reaches their phone.
url (max. 2)texts, urlThe link opens (and you receive message.button_clicked with the final URL).

url rules: https:// only, with a domain name (not an IP), no user or password and no URL shorteners (bit.ly, tinyurl.com, t.co…). It can contain one {{1}} variable in the path or the query (never in the domain), e.g. https://tiendaexpress.example.com/pedidos/{{1}}. Its value is sent with each message in button_variables, by button index:

{"to": "+573005550123", "sender_id": "tiendaexpress", "template": "pedido_enviado",
 "variables": ["Valentina", "TE-48213"], "button_variables": {"0": "TE-48213"}}

The value is text (max. 200 characters) and xYago URL-encodes it. A missing value for a button with a variable, an unexpected one or an invalid one → 422 invalid_button_variables.

Example template (GET /templates/{id}):

{
  "id": "d2f4a6c8-0e1b-4d3f-9a5c-7e9b1d3f5a70",
  "sender_id": "tiendaexpress",
  "name": "pedido_enviado",
  "category": "utility",
  "default_language": "es",
  "languages": {
    "es": "Hola {{1}}, tu pedido {{2}} va en camino.",
    "en": "Hi {{1}}, your order {{2}} is on its way."
  },
  "variables_count": 2,
  "buttons": [
    {
      "type": "url",
      "texts": {
        "es": "Seguir pedido",
        "en": "Track order"
      },
      "payload": null,
      "url": "https://tiendaexpress.example.com/pedidos/{{1}}"
    },
    {
      "type": "quick_reply",
      "texts": {
        "es": "No lo pedí",
        "en": "Not my order"
      },
      "payload": "order_not_mine",
      "url": null
    }
  ],
  "version": 1,
  "status": "approved",
  "review_flags": [],
  "rejection_reason": null,
  "created_at": "2026-11-03T10:00:00Z",
  "updated_at": "2026-11-03T16:45:00Z"
}

Review by xYago

  1. You create the template (draft) and submit it for review (pending_review). In sandbox you can already test a draft.
  2. On submission automatic checks (review_flags) are computed; the reviewer and you see them in the panel. They do not block the submission, but you should fix them:
FlagMeaning
marketing_wordsAdvertising words (discount, offer, free, coupon…). Templates are for service notifications only.
url_shortenerMentions a URL shortener.
too_many_variablesMore than 10 variables.
variables_adjacentTwo variables in a row without text between them.
starts_or_ends_with_variableThe text starts or ends with a variable.
only_variablesThe text is only variables.

xYago approves it (approved) or rejects it with a reason (rejected; fix it in the panel and it goes back to review). Every step is kept in its history in the panel and emits template.status_changed.

Versions

An approved template does not change. Editing it creates the next version (same name, version + 1) in review, and the approved one keeps being used for your sends until xYago approves the new one; then the previous one becomes superseded. Nothing changes in your integration: you send by name.

If xYago pauses a template (e.g. after user complaints), sends with it return 409 template_paused until it is reactivated.

Statuses

StatusMeaning
draftDraft (sandbox only).
pending_reviewBeing reviewed by xYago.
approvedUsable in live and sandbox.
rejectedRejected (see rejection_reason).
pausedPaused by xYago: 409 template_paused.
supersededReplaced by a newer approved version.

Click webhook

Subscribe to message.button_clicked. Only the first tap of each button of each message is reported. data contains the message (as in message.*, with your reference), the button and the click time. The button text is the one in the language the message was sent in (button.language, same as message.language):

{
  "created_at": "2026-03-02T15:00:04Z",
  "data": {
    "message": {
      "id": "7a1c9e35-2b4d-4f6a-8c0e-1d3f5a7b9c21",
      "status": "read",
      "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": "2026-11-04T15:02:10Z",
      "read_at": "2026-11-04T15:07:42Z",
      "expires_at": null
    },
    "button": {
      "index": 1,
      "id": "b1",
      "type": "quick_reply",
      "text": "No lo pedí",
      "language": "es",
      "payload": "order_not_mine",
      "url": null
    },
    "clicked_at": "2026-11-04T15:08:03Z"
  },
  "environment": "live",
  "id": "evt_4f9c2b7a1d3e4c5b8a6f0e1d2c3b4a59",
  "type": "message.button_clicked"
}