API reference - Conversations, messages, and attachments

API reference · OpenAPI specification

Endpoint definitions below are generated from the API contract. Request and response blocks describe schemas, not example payloads. required lists mandatory fields; $ref refers to a linked model. Send JSON bodies with Content-Type: application/json unless the endpoint specifies binary content.

List accessible external conversation messages

GET /v1/messages

Requires messages.read. Every inbox in the conversation must be an allowed, current inbox. Internal-only conversations are excluded. Returns individual existing Waveline message records, not the logical recipient-group aggregation proposed in the full plan. external_number matches a participant in the conversation, not a claimed per-message recipient.

Parameters

NameLocationRequiredDescription and constraints
inbox_idqueryNoFilter to activity involving this accessible inbox. Does not change connection access or the returned resource projection. Stable existing Waveline record ID. Keep it as a string; it may exceed JavaScript's safe integer range. Type: string maxLength: 19 pattern: "^[1-9][0-9]*$"
directionqueryNoFilter by external call or message direction. Type: string enum: ["inbound","outbound"]
limitqueryNoType: integer default: 50 minimum: 1 maximum: 100
cursorqueryNoOpaque cursor from next_cursor. Reuse the same credentials, endpoint, filters, ordering, and limit. Results may change between requests; pagination does not freeze the dataset. Type: string
created_afterqueryNoInclusive lower time bound. Applies to start_time for calls and created_at for contacts/messages. Include a time zone. Type: string format: "date-time"
created_beforequeryNoExclusive upper time bound; must be later than created_after. Include a time zone. Type: string format: "date-time"
external_numberqueryNoExact external conversation participant phone number. Type: string pattern: "^\+[1-9][0-9]{1,14}$"
conversation_idqueryNoOnly messages from this authorized conversation. All filters combine with AND. Stable existing Waveline record ID. Keep it as a string; it may exceed JavaScript's safe integer range. Type: string maxLength: 19 pattern: "^[1-9][0-9]*$"
orderqueryNooldest (the default) pages forward by ascending stable ID. newest returns the most recent records first and next_cursor continues with older records. The order is bound to the cursor. Type: string default: "oldest" enum: ["oldest","newest"]
include_labelsqueryNotrue adds display labels to each record: inbox_labels for accessible inboxes, contacts matched to external numbers (requires contacts.read; omitted otherwise) and, for messages, sender_name. The default record shape and webhook payloads are unchanged. Type: string default: "false" enum: ["true","false"]

Responses

200 — Messages in the requested order (ascending stable ID by default).

application/json

allOf:
  - $ref: '#/components/schemas/Page'
  - properties:
      data:
        type: array
        items:
          $ref: '#/components/schemas/Message'

Models: Page, Message.

Errors — 400 invalid input, 401 expired/revoked/missing key, 403 scope or access denied, 404 missing/inaccessible, 409 request/state conflict, 413 oversized request or response, 415 wrong content type, 429 rate limited, or 503 unavailable. 429 includes Retry-After. All responses include X-Request-Id.

Error format, status codes, and retry guidance.

Send one billed external recipient message

POST /v1/messages

Requires messages.write and messages.read, a selected inbox with a registered sending number, and an invoice-backed subscription. Each accepted recipient send costs US$0.01 before tax, including sends that later fail delivery. Segments, attachment parts, and retries under the same Idempotency-Key do not add integration sending fees. Send deduplication is permanent. Each request supports one E.164 external recipient and must include text or attachments. Messaging registration, opt-out, and recipient-blocking requirements still apply. Acceptance does not confirm delivery.

Parameters

NameLocationRequiredDescription and constraints
Idempotency-KeyheaderYesUse a stable request identifier. Contact writes replay the saved response for seven days, subject to current resource access. Subscription creation retains its identity for seven days but returns current metadata and signing secret; a disabled/deleted subscription rejects replay. Reusing those keys with a different operation, path or exact JSON body returns 409. Subscription status and delete operations are repeatable state changes. Type: string minLength: 8 maxLength: 128 pattern: "^[!-~]+$"

Request body (required)

application/json

type: object
required:
  - inbox_id
  - to
properties:
  inbox_id:
    $ref: '#/components/schemas/RecordId'
  to:
    type: string
    pattern: ^\+[1-9][0-9]{1,14}$
  body:
    type: string
    maxLength: 1600
  from:
    type: string
  attachment_ids:
    type: array
    maxItems: 10
    items:
      $ref: '#/components/schemas/RecordId'
    description: >-
      Finalized upload attachment IDs for this connection/inbox or attachments on
      currently accessible messages. Total<=5MiB. Each upload is consumed by one logical
      send.
additionalProperties: false

Models: RecordId.

Responses

202 — Authorized result.

application/json

type: object
required:
  - data
properties:
  data:
    $ref: '#/components/schemas/MessageSend'

Models: MessageSend.

Errors — 400 invalid input, 401 expired/revoked/missing key, 403 scope or access denied, 404 missing/inaccessible, 409 request/state conflict, 413 oversized request or response, 415 wrong content type, 429 rate limited, or 503 unavailable. 429 includes Retry-After. All responses include X-Request-Id.

Error format, status codes, and retry guidance.

Get one accessible message

GET /v1/messages/{id}

Requires messages.read. Includes completed attachment metadata; download URLs require a separate authorized request.

Parameters

NameLocationRequiredDescription and constraints
idpathYesStable existing Waveline record ID. Keep it as a string; it may exceed JavaScript's safe integer range. Type: string maxLength: 19 pattern: "^[1-9][0-9]*$"
include_labelsqueryNotrue adds display labels to each record: inbox_labels for accessible inboxes, contacts matched to external numbers (requires contacts.read; omitted otherwise) and, for messages, sender_name. The default record shape and webhook payloads are unchanged. Type: string default: "false" enum: ["true","false"]

Responses

200 — Message record.

application/json

type: object
required:
  - data
properties:
  data:
    $ref: '#/components/schemas/Message'
  rate_limit:
    $ref: '#/components/schemas/RateLimit'

Models: Message, RateLimit.

Errors — 400 invalid input, 401 expired/revoked/missing key, 403 scope or access denied, 404 missing/inaccessible, 409 request/state conflict, 413 oversized request or response, 415 wrong content type, 429 rate limited, or 503 unavailable. 429 includes Retry-After. All responses include X-Request-Id.

Error format, status codes, and retry guidance.

List accessible conversations by recent activity

GET /v1/conversations

Requires messages.read. Returns one row per readable conversation, most recent activity first, using the same visibility rules as listMessages. Each row carries a preview of the last message and awaiting_reply (the external party sent the last message). Personal assistant (OAuth) connections also receive the member's own unread state; API keys receive null and cannot use unread_only. The date filters apply to the last activity. The cursor is anchored to the last returned conversation's current activity, so a conversation that receives a new message while you page can appear again; none are skipped.

Parameters

NameLocationRequiredDescription and constraints
inbox_idqueryNoFilter to activity involving this accessible inbox. Does not change connection access or the returned resource projection. Stable existing Waveline record ID. Keep it as a string; it may exceed JavaScript's safe integer range. Type: string maxLength: 19 pattern: "^[1-9][0-9]*$"
limitqueryNoType: integer default: 50 minimum: 1 maximum: 100
cursorqueryNoOpaque cursor from next_cursor. Reuse the same credentials, endpoint, filters, ordering, and limit. Results may change between requests; pagination does not freeze the dataset. Type: string
created_afterqueryNoInclusive lower time bound. Applies to start_time for calls and created_at for contacts/messages. Include a time zone. Type: string format: "date-time"
created_beforequeryNoExclusive upper time bound; must be later than created_after. Include a time zone. Type: string format: "date-time"
include_labelsqueryNotrue adds display labels to each record: inbox_labels for accessible inboxes, contacts matched to external numbers (requires contacts.read; omitted otherwise) and, for messages, sender_name. The default record shape and webhook payloads are unchanged. Type: string default: "false" enum: ["true","false"]
external_numberqueryNoExact external conversation participant phone number. Type: string pattern: "^\+[1-9][0-9]{1,14}$"
unread_onlyqueryNotrue returns only conversations unread by the authenticated member. Personal assistant connections only. Type: string enum: ["true","false"]

Responses

200 — Conversations ordered by most recent activity.

application/json

allOf:
  - $ref: '#/components/schemas/Page'
  - properties:
      data:
        type: array
        items:
          $ref: '#/components/schemas/Conversation'

Models: Page, Conversation.

Errors — 400 invalid input, 401 expired/revoked/missing key, 403 scope or access denied, 404 missing/inaccessible, 409 request/state conflict, 413 oversized request or response, 415 wrong content type, 429 rate limited, or 503 unavailable. 429 includes Retry-After. All responses include X-Request-Id.

Error format, status codes, and retry guidance.

List completed message attachments

GET /v1/messages/{id}/attachments

Requires messages.read. Missing and inaccessible parent resources return 404.

Parameters

NameLocationRequiredDescription and constraints
idpathYesStable existing Waveline record ID. Keep it as a string; it may exceed JavaScript's safe integer range. Type: string maxLength: 19 pattern: "^[1-9][0-9]*$"
limitqueryNoType: integer default: 50 minimum: 1 maximum: 100
cursorqueryNoOpaque cursor from next_cursor. Reuse the same credentials, endpoint, filters, ordering, and limit. Results may change between requests; pagination does not freeze the dataset. Type: string

Responses

200 — Page ordered by ascending ID.

application/json

type: object
required:
  - data
  - next_cursor
properties:
  data:
    type: array
    items:
      $ref: '#/components/schemas/MessageAttachment'
  next_cursor:
    type:
      - string
      - 'null'

Models: MessageAttachment.

Errors — 400 invalid input, 401 expired/revoked/missing key, 403 scope or access denied, 404 missing/inaccessible, 409 request/state conflict, 413 oversized request or response, 415 wrong content type, 429 rate limited, or 503 unavailable. 429 includes Retry-After. All responses include X-Request-Id.

Error format, status codes, and retry guidance.

Get a private message attachment download URL

GET /v1/attachments/{id}/download

Requires messages.read and current access to the associated message. The URL expires after five minutes.

Parameters

NameLocationRequiredDescription and constraints
idpathYesStable existing Waveline record ID. Keep it as a string; it may exceed JavaScript's safe integer range. Type: string maxLength: 19 pattern: "^[1-9][0-9]*$"

Responses

200 — Authorized result.

application/json

type: object
required:
  - data
properties:
  data:
    $ref: '#/components/schemas/MediaDownload'

Models: MediaDownload.

Errors — 400 invalid input, 401 expired/revoked/missing key, 403 scope or access denied, 404 missing/inaccessible, 409 request/state conflict, 413 oversized request or response, 415 wrong content type, 429 rate limited, or 503 unavailable. 429 includes Retry-After. All responses include X-Request-Id.

Error format, status codes, and retry guidance.

Reserve a message media upload

POST /v1/message-uploads

Requires messages.read+messages.write and an allowed inbox. Expires in 24 hours; at most 25 unused unexpired reservations/connection. Uploading does not incur the sending fee. Only ready media can be sent, once per upload.

Parameters

NameLocationRequiredDescription and constraints
Idempotency-KeyheaderYesUse a stable request identifier. Contact writes replay the saved response for seven days, subject to current resource access. Subscription creation retains its identity for seven days but returns current metadata and signing secret; a disabled/deleted subscription rejects replay. Reusing those keys with a different operation, path or exact JSON body returns 409. Subscription status and delete operations are repeatable state changes. Type: string minLength: 8 maxLength: 128 pattern: "^[!-~]+$"

Request body (required)

application/json

type: object
required:
  - inbox_id
  - file_name
  - mime_type
  - size_bytes
  - sha256
properties:
  inbox_id:
    $ref: '#/components/schemas/RecordId'
  file_name:
    type: string
    pattern: ^[A-Za-z0-9][A-Za-z0-9._ -]{0,119}$
  mime_type:
    type: string
    enum:
      - image/jpeg
      - image/png
      - image/gif
      - image/webp
      - application/pdf
      - audio/mpeg
      - audio/mp4
      - video/mp4
  size_bytes:
    type: integer
    minimum: 1
    maximum: 5242880
  sha256:
    type: string
    pattern: ^[a-f0-9]{64}$
additionalProperties: false

Models: RecordId.

Responses

201 — Authorized result.

application/json

type: object
required:
  - data
properties:
  data:
    $ref: '#/components/schemas/MessageUpload'

Models: MessageUpload.

Errors — 400 invalid input, 401 expired/revoked/missing key, 403 scope or access denied, 404 missing/inaccessible, 409 request/state conflict, 413 oversized request or response, 415 wrong content type, 429 rate limited, or 503 unavailable. 429 includes Retry-After. All responses include X-Request-Id.

Error format, status codes, and retry guidance.

Upload exactly the reserved media bytes

PUT /v1/message-uploads/{upload_id}/content

Use reserved Content-Type, same Bearer credentials, exact size and SHA-256. Content-Encoding is rejected. Retry identical bytes at this URL; existing objects are never overwritten. Returns ready attachment_id. The upload ID provides idempotency; no separate Idempotency-Key is needed.

Parameters

NameLocationRequiredDescription and constraints
upload_idpathYesType: string format: "uuid"

Request body (required)

image/jpeg, image/png, image/gif, image/webp, application/pdf, audio/mpeg, audio/mp4, video/mp4

type: string
format: binary

Responses

200 — Authorized result.

application/json

type: object
required:
  - data
properties:
  data:
    $ref: '#/components/schemas/MessageUpload'

Models: MessageUpload.

Errors — 400 invalid input, 401 expired/revoked/missing key, 403 scope or access denied, 404 missing/inaccessible, 409 request/state conflict, 413 oversized request or response, 415 wrong content type, 429 rate limited, or 503 unavailable. 429 includes Retry-After. All responses include X-Request-Id.

Error format, status codes, and retry guidance.