Skip to content
Linq

Invitations

An invitation is the only way to initiate contact. It sends a templated card to a phone number, not a free-text message.

Setup, once: create an invitation template: the brand name printed on the card, and optionally your logo, uploaded in the same request:

POST /v1/invitation_templates
Content-Type: multipart/form-data

name = "Acme Rides"
logo = <PNG file, optional>

The response carries the template’s id (invt_…). A template with a logo renders the card with your image; one without renders the plain card: the platform picks the right card for you. If creation is refused 403 invitation_card_not_granted, your brand is not yet approved for invitations of that shape: approval is per-brand and raised with us, not self-service.

Then, per invitation:

POST /v1/invitations
Idempotency-Key: 7c9e6679-7425-40de-944b-e07fc1f90ae7

{"to": "+15551234567", "template_id": "invt_a4f2c718d0", "reference_id": "order-4417"}

Two conditions apply:

  • A recorded opt-in for that number, posted to POST /v1/consent with subject_kind: "tel". Without one the send returns 403 consent_required.
  • One of your template ids. GET /v1/invitation_templates lists them.

If the customer accepts, a chat is created and you message it normally. The outcome arrives as invitation.accepted, invitation.declined, or invitation.opted_out.

POST /v1/invitation_batches submits up to 10,000 invitations at once, all from one template: its name and logo are resolved when you submit and frozen into the run, so editing the template mid-run never changes what queued recipients receive. We check every recipient when you submit, so you learn which ones will fail before we send anything. Track the run by its id rather than per message, and POST /v1/invitation_batches/{batch}/cancel stops any recipient we haven’t tried yet.


Send an invitation
POST/v1/invitations
Get an invitation
GET/v1/invitations/{inv}
ModelsExpand Collapse
InvitationCreateResponse object { id, chat_id, delivery, 2 more }

The accepted invitation. A replay of the same Idempotency-Key within 24h returns these exact bytes; past that window the key is forgotten and the request executes again.

id: string

Invitation id (inv_…).

chat_id: string

The tel-handled chat carrying the invitation delivery; NOT addressable for organic sends.

delivery: "queued"
message_id: string

The invitation delivery’s message id; poll it via GET /v1/messages/{msg}.

to: string

The canonical tel:+E164 handle form of the request’s to.

InvitationRetrieveResponse object { id, chat_id, created_at, 9 more }

The composed invitation status; every field derived at read time from the table that owns it, so nothing can drift.

id: string

Invitation id (inv_…).

chat_id: string
created_at: string
formatdate-time
delivery: string

The underlying message’s delivery state; same values as GET /v1/messages/{msg}.

opted_out: boolean

Derived from the consent record: a later tel STOP flips it. Independent of response.

parameters: unknown

The parameters as sent (JSON null when none).

reference_id: string
response: string

none, accepted, declined, or superseded (an outstanding card on a thread a sibling’s accept superseded).

response_source: string

How response was resolved: tap; Apple forwarded the customer’s own interaction and we correlated it by the card’s requestIdentifier; inference; we concluded it from message ordering (a reply on the thread while the card was outstanding), which a later tap may correct; none; unresolved, and for the derived superseded response, which is a fact about the chat rather than an answer from the customer.

Reconcile on this when a deduced decline must not be treated like an answered one.

template_id: string
accepted_chat_id: optional string

The opaque chat the customer landed in; this card’s accept, or the sibling accept that superseded it.

locale: optional string

Omitted when the send carried none.