Release Candidate — the API is undergoing final validation before general availability, and minor changes may still occur.
Hellomateo API Documentation
Recipes

Poll opt-in changes reliably

Use audit logs to incrementally process opt-in creations, status changes, and deletions.

Use GET /v1/audit-logs to keep opt-in state synchronized. Poll with a small time overlap and deduplicate events by audit-log id because audit-log ingestion is asynchronous.

Query the change feed

For an organisation-wide incremental poll, always provide a bounded time range. Use RFC3339 timestamps for both bounds, and restrict the query to the opt-in events:

  • action[in]=opt_in.created,opt_in.status_changed,opt_in.deleted
  • subject_type=opt_in
  • occurred_at[gte] and occurred_at[lte]
  • include=subjects

For example:

curl --get 'https://api.getmateo.com/v1/audit-logs' \
  --header "Authorization: Bearer $MATEO_API_TOKEN" \
  --header "hellomateo-version: 2026-07-01" \
  --data-urlencode 'action[in]=opt_in.created,opt_in.status_changed,opt_in.deleted' \
  --data-urlencode 'subject_type=opt_in' \
  --data-urlencode 'occurred_at[gte]=2026-01-01T00:00:00Z' \
  --data-urlencode 'occurred_at[lte]=2026-01-01T01:00:00Z' \
  --data-urlencode 'include=subjects' \
  --data-urlencode 'limit=100'

The endpoint orders results newest first, by occurred_at and then by event id. Follow next_page_url exactly as returned until it is null; do not construct page URLs or use offsets. An abbreviated page contains the audit event's primary opt-in subject and, when present, related contact and opt-in-group subjects:

{
  "object": "list",
  "data": [
    {
      "id": "01945f2e-7b7d-7a10-9f30-123456789abc",
      "occurred_at": "2026-01-01T00:42:00Z",
      "action": "opt_in.status_changed",
      "actor": { "type": "employee", "id": "employee-123" },
      "changes": [
        { "field": "status", "before": "requested", "after": "accepted" }
      ],
      "subjects": [
        {
          "subject_type": "opt_in",
          "subject_id": "opt-in-123",
          "subject_role": "primary",
          "external_id": null,
          "secondary_external_id": null
        },
        {
          "subject_type": "contact",
          "subject_id": "contact-456",
          "subject_role": "related",
          "external_id": null,
          "secondary_external_id": null
        },
        {
          "subject_type": "opt_in_group",
          "subject_id": "group-789",
          "subject_role": "related",
          "external_id": null,
          "secondary_external_id": null
        }
      ]
    }
  ],
  "next_page_url": "/v1/audit-logs?...",
  "previous_page_url": null
}

Process changes

For each poll, start a few minutes before the previous poll ended and use the current time as the upper bound. Follow every next_page_url, deduplicate results by audit-log id, and process them oldest first. Each subject's external_id is its primary identifier and secondary_external_id is its secondary identifier, both captured at the point in time of the audit event. Both fields are always present and are null when the corresponding identifier is unavailable.

See List Audit Logs for the complete request and response contract, and Pagination for shared cursor-pagination behavior.

On this page