Back to top

API Documentation

Communications

Overview

A communication is one entry in a client’s call or text log: a single call, or a single message, together with the parties involved. Clients push their logs here so the records can appear on a contact’s timeline.

Field Accepted values
kind Call, Message
direction Incoming, Outgoing, Missed, Unknown
participants[].identifier_type Phone, Email

Create communications

Create communications
POST/brands/:brand/communications

Example URI

POST /brands/:brand/communications
URI Parameters
HideShow
brand
string (required) Example: 83fa26db-10ce-4869-ae24-a5bd9c210a73
Request
HideShow
Body
[
  {
    "idempotency_key": "call:4821:1725360000000",
    "source_record_id": "4821",
    "kind": "Call",
    "occurred_at": "2026-09-01T10:40:00Z",
    "direction": "Incoming",
    "participants": [
      {
        "identifier": "+12025550123",
        "identifier_type": "Phone",
        "role": "from",
        "display_name": "Synthetic Contact"
      }
    ],
    "call": {
      "duration_seconds": 83,
      "outcome": "completed",
      "service": "cellular"
    }
  },
  {
    "idempotency_key": "e0042e06104eabe70c2a70cf3695a1d8",
    "source_record_id": "message-record-example-1",
    "kind": "Message",
    "occurred_at": "2026-09-01T10:41:00.123Z",
    "direction": "Outgoing",
    "participants": [
      {
        "identifier": "[email protected]",
        "identifier_type": "Email"
      }
    ],
    "message": {
      "text": "Synthetic contract-test message.",
      "service": "imessage",
      "conversation_id": "conversation-example-1",
      "delivery": "sent"
    }
  }
]
Response  204
HideShow
Body
| Field | Type | Notes |
| --- | --- | --- |
| `idempotency_key` | String, required | Max 128 characters. Treated as opaque — not parsed. |
| `source_record_id` | String, required | Max 255 characters. The client's own stable ID for the record. |
| `kind` | Enum, required | `Call` or `Message`. Determines which block applies. |
| `occurred_at` | String, required | ISO 8601 **with an offset** (`Z`, `+02:00`). A value with no offset is rejected, because it is ambiguous. |
| `direction` | Enum, required | Relative to the owner. |
| `participants` | Array, required | At least one. A participant needs `identifier` and `identifier_type`; `role` and `display_name` are optional. |
| `call` | Object | Required when `kind` is `Call`. |
| `message` | Object | Required when `kind` is `Message`. |

Call block:

| Field | Type |
| --- | --- |
| `duration_seconds` | Integer |
| `outcome` | String |
| `service` | String |

Message block:

| Field | Type |
| --- | --- |
| `text` | String |
| `service` | String |
| `delivery` | String |
| `conversation_id` | String |

`service`, `outcome` and `delivery` are free text: they are owned by the
platforms, so unknown values are stored rather than rejected.

Properties that are not listed above are ignored. `brand` and `owner` are
resolved server-side and have no effect if sent.

Get the latest communication

Get the latest communication
GET/brands/:brand/communications/latest

Reports where your sync currently stands, as a cursor to resume from.

Scoped to the authenticated user, exactly like ingest: this returns your own sync position, never another agent’s.

Example URI

GET /brands/:brand/communications/latest
URI Parameters
HideShow
brand
string (required) Example: 83fa26db-10ce-4869-ae24-a5bd9c210a73
Response  200
HideShow
Body
{
          "code": "OK",
          "data": {
            "type": "communication",
            "id": "e7547d74-88a2-432a-a8fe-6d2a5ca78492",
            "brand": "83fa26db-10ce-4869-ae24-a5bd9c210a73",
            "source": "default",
            "kind": "Message",
            "source_record_id": "message-record-example-1",
            "idempotency_key": "e0042e06104eabe70c2a70cf3695a1d8",
            "direction": "Outgoing",
            "owner": "b71a204c-688f-4b48-aacc-29113e595ee3",
            "attachments": [],
            "occurred_at": 1788259260,
            "created_at": 1789624522,
            "updated_at": 1789624522,
            "deleted_at": null,
            "duration_seconds": null,
            "outcome": null,
            "call_service": null,
            "text": "Synthetic contract-test message.",
            "message_service": "imessage",
            "delivery": "sent",
            "conversation_id": "conversation-example-1",
            "participants": [
              {
                "ordinal": 0,
                "identifier": "[email protected]",
                "identifier_type": "Email",
                "normalized_identifier": "[email protected]",
                "role": null,
                "display_name": null
              }
            ]
          }
        }

| Field | Notes |
| --- | --- |
| `cursor` | Opaque token for your sync position. Treat it as a token to hand back, not something to parse or build. `null` when you have no records yet. |

The cursor points at the record with the greatest `occurred_at`, and names it by
the `kind` and `source_record_id` you sent — so every value in it is one you
recognise, and no server-side id is exposed.

`occurred_at`, `kind` and `source_record_id` together form the ordering key, not
`occurred_at` alone: two records can share a timestamp, and a cursor that could
not separate them would skip or repeat records.

Generated by aglio on 17 Sep 2026