ErrorEnvelope
successcodeMirrors the HTTP status code.
messageHuman-readable error. Do not branch logic on this string β branch on code.
Pagination
current_pageper_pagetotal_itemstotal_pageshas_prev_pagehas_next_pageUserSlim
idfull_nameavatar_urlUser
idemailfull_nameavatar_urlphonejob_titlerolestatusorganization_idLoginRequest
emailLowercased server-side for comparison.
passwordSent in plaintext over TLS only.
LoginResponseData
access_tokenJWT signed with OPENAPI_JWT_SECRET_KEY. 1-hour TTL.
token_typeexpires_atAbsolute access-token expiry.
refresh_tokenOpaque cmrr_<prefix>_<secret> token. 30-day TTL. Rotated on every refresh β replace your stored copy after every /auth/refresh call.
refresh_expires_atFull user profile.
LoginChallengeData
requires_2fachallenge_tokenSingle-use 2FA challenge JWT (~5 min TTL). Not usable as an access token.
expires_atTwoFAVerifyRequest
challenge_tokenThe JWT returned by the 2FA branch of /auth/login.
code6-digit TOTP from the authenticator app, OR an alphanumeric recovery code. Server tries TOTP first then falls back to recovery β the client does not need to specify which.
LogoutRequest
refresh_tokenRefresh row to revoke. Even when all_devices=true, the field is required as proof-of-possession.
all_devicesWhen true, revokes every active refresh row for the user β useful for a Sign out everywhere button.
MeResponseData
Full user profile.
auth_methodWhich mechanism authenticated this call β useful for integrators to confirm their API key is recognized.
HomeSummaryData
limit: null means unlimited (Enterprise). Render an infinity glyph instead of zero.
MetricsBucket
bucket_startlabelPre-formatted for the chart axis β "14 May" for daily buckets, "13:00" for hourly.
totalConversationMetricsData
rangetotalPage-level sum. Always equals the sum of buckets[].total.
AgentPerformanceItem
idnameavatar_urltotal_assignedConversations created in the period that have assigned_agent_id set.
total_resolvedSubset of total_assigned whose resolved_at also falls inside the period.
ContactSlim
idnameavatar_urlphoneChannelType
Wire enum identifying the channel brand. Pass to ?channel_type= on GET /conversations.
ChannelSlim
idnametypeWire enum identifying the channel brand. Pass to ?channel_type= on GET /conversations.
Inbox
idnametypeWire enum identifying the channel brand. Pass to ?channel_type= on GET /conversations.
statusInbox operational state.
AgentDropdownItem
idnameemailroleavatar_urlChannelTypeRow
typeWire enum identifying the channel brand. Pass to ?channel_type= on GET /conversations.
display_nameServer-supplied human label β mirrors the dashboard's getChannelDisplayName().
brand_colorHex with # prefix. Falls back to #6B7280 (neutral grey) for future channel types.
active_inbox_countNumber of ACTIVE inboxes the organization has of this brand.
Team
idnamedescriptionmember_countACTIVE members for the team β resolved via a single batched COUNT GROUP BY team_id per page.
auto_distributeWhen true, assigning a conversation round-robins one of the team's active members. When false, the team owns the chat collectively until an agent claims it manually.
created_atCampaign
idnamestatuscampaign_typecreated_atAIAgentDropdownItem
idnamestatusConversationStatusRow
statusdisplay_namecolorBrand color for the status pill / dot.
WhatsAppTemplate
idorganization_idchannel_idnameMeta-approved template name.
languageBCP-47 β id, en_US, etc.
categorystatusRaw Meta components[] JSON. Renderers read type, format, text, buttons[], example.header_handle[0].
ConversationListItem
idstatusassignment_statusEchoes the tab you requested.
priorityunread_countCONTACT-sender, non-private, no read_at. Surfaced from the Redis counter (falls back to the SQL mirror).
last_message_atlast_messagePreview of the most recent non-private message. Null when the conversation has no messages yet.
Embedded contact reference on conversations and messages. Always present β name falls back to phone, then "Unknown Contact".
Embedded channel reference on conversations.
Minimal user identity β the shape used inside conversation rows, ticket assignees, and chat sender refs.
created_atupdated_atis_auto_repliedWhether the AI agent is set to auto-reply on this conversation. Toggle via PATCH /conversations/{id}/ai-agent.
is_unreadSQL mirror of chatmo:ws:unread:{id} > 0. Powers the is_unread=true filter.
ConversationSearchItem
idstatusassignment_statusEchoes the tab you requested.
priorityunread_countCONTACT-sender, non-private, no read_at. Surfaced from the Redis counter (falls back to the SQL mirror).
last_message_atlast_messagePreview of the most recent non-private message. Null when the conversation has no messages yet.
Embedded contact reference on conversations and messages. Always present β name falls back to phone, then "Unknown Contact".
Embedded channel reference on conversations.
Minimal user identity β the shape used inside conversation rows, ticket assignees, and chat sender refs.
created_atupdated_atis_auto_repliedWhether the AI agent is set to auto-reply on this conversation. Toggle via PATCH /conversations/{id}/ai-agent.
is_unreadSQL mirror of chatmo:ws:unread:{id} > 0. Powers the is_unread=true filter.
match_typeWhich arm of the OR matched: contact name/phone, message content, or both.
matched_messageSlim preview of the most recent message whose content matched q. Present when match_type is "message" or "both", null when "name".
MessageReaction
reactor_typereactor_external_idreactor_nameemojiMessage
idconversation_idsenderContact-side sender. Populated for sender_type=CONTACT.
sender_agentAgent-side sender. Populated for sender_type=AGENT / AI_AGENT.
sender_typemessage_typesystem_typeFor sender_type=SYSTEM.
contentmedia_urlmedia_metadataChannel-specific extras β filename, dimensions, duration, etc.
thumbnail_media_urlmedia_attachmentJoined attachment row on the message. Server generates thumbnails for IMAGE / VIDEO synchronously under 25 MB, asynchronously otherwise.
statusexternal_message_idChannel-side message id β WhatsApp wamid.*, Telegram numeric, FB/IG MID.
error_codeerror_titleerror_messageerror_detailsis_automatedTrue for AI-generated or scheduler-driven messages.
is_privateAlways false on this endpoint β private messages are filtered server-side.
metadataPass-through bag β template name, button payload, etc.
bubble_group_idbubble_sequenceis_bubble_messageis_replyConvenience: reply_to != null.
reply_toInline preview of the parent message. Includes media URL + thumbnail + filename so WhatsApp-style reply quotes render without a second lookup.
sent_atdelivered_atread_atcreated_atLabel
idorganization_idnamecolor^#[0-9a-fA-F]{6}$assignments_countActive rows in conversation_label_assignments pointing at this label.
created_atupdated_atLabelAssignment
idJunction row id (conversation_label_assignments.id).
label_idCatalog id β use this to identify the chip across conversations.
namecolor^#[0-9a-fA-F]{6}$assigned_atassigned_byCollaborator
iduser_idstatusmessage_countlast_message_atadded_atBulkUpdateConversationsRequest
conversation_idsTargets. Capped at 50 β fan-out runs the matching single-action usecase per row.
BulkActionItem
conversation_idsuccessreasonPopulated when success=false.
addedPopulated for additive ops β collaborators added, labels attached.
skippedPre-existing assignments that were left alone.
InitiateConversationRequest
channel_idMust be a WHATSAPP_OFFICIAL or WHATSAPP_UNOFFICIAL channel in your organization.
country_codeE.164 dial code with leading +.
phoneLocal digits only β country code lives in the field above.
nameContact display name. Used when find-or-create creates a new contact; ignored when the contact already exists.
InitiateConversationData
is_new_contactis_new_conversationtrue for 201 Created, false for 200 OK (existing conversation returned).
UploadFileResult
idPaste this into message.send's media.file_id to reference the upload.
file_reffile_nameoriginal_namecategorymime_typefile_sizestorage_urlhas_thumbnailthumbnail_urlthumbnail_sizestatuscreated_atUploadsData
organization_idconversation_idPer-file failures so one bad file does not sink a 5-file upload.
TicketBoardStatus
idboard_idnamecolorpositionis_defaultarchived_atticket_countcreated_atupdated_atTicketBoard
idorganization_idnamedescriptionis_defaultpositioncreated_bycreated_atupdated_atticket_countPresent when the request was made with include_statuses=true. Omitted (not null, not empty array) otherwise.
TicketTemplateField
idtemplate_idfield_namefield_typedefault_valueoptionsis_requiredpositionTicketTemplate
idorganization_idboard_idnametypedefault_prioritydefault_status_idcreated_bycreated_atupdated_atdefault_statusProduct
idorganization_idnamedescriptionpriceJSON number β decimal converted to float64. Matches dashboard cart math.
product_typeweightGrams β only meaningful for PHYSICAL.
stockimagesis_activecreated_atupdated_atdeleted_atTicketAssignee
idticket_iduser_idassigned_atMinimal user identity β the shape used inside conversation rows, ticket assignees, and chat sender refs.
Ticket
idorganization_idboard_idtemplate_idstatus_idticket_numberAuto-allocated MAX(ticket_number)+1 within the org.
titledescriptionpriorityassigned_toLegacy single assignee.
conversation_idis_donedone_atpositionWithin the status column, auto-allocated MAX(position)+1.
created_bycreated_atupdated_atMinimal user identity β the shape used inside conversation rows, ticket assignees, and chat sender refs.
created_by_userconversationTicketCreateRequest
board_idstatus_idMust belong to board_id.
titledescriptionprioritytemplate_idassigned_toLegacy single assignee.
assigned_to_idsMulti-assignee array. Inserted into ticket_assignees.
conversation_idTicketUpdateRequest
titledescriptionPass empty string to clear.
prioritystatus_idMust belong to the same board. Flips is_done if the target status is the board's is_default.
assigned_toassigned_to_idsWhen present, REPLACES ticket_assignees (delete-all + create-all in one transaction).
conversation_idtemplate_idTicketActivity
idticket_iduser_idtypecontentmetadataEmpty {} for COMMENT. For CHANGELOG: {field, old, new}.
created_atupdated_atMinimal user identity β the shape used inside conversation rows, ticket assignees, and chat sender refs.
TicketCommentCreateRequest
contentmentioned_user_idsFrontend-pre-parsed @[Name](uuid) tokens. Server does NOT re-parse content.
TicketCommentUpdateRequest
contentmentioned_user_idsReplacement semantics β omit to leave existing mentions untouched; [] to clear; [a,b] to replace.
TicketAttachment
idticket_idfile_nameSanitized original filename. Bucket key gets a timestamp prefix internally to dodge collisions.
file_urlfile_sizemime_typeuploaded_bycreated_atContact
idexternal_idnameemailphoneavatar_urlstatuscreated_atupdated_atOriginAddress
idorganization_idnamecontact_namephoneaddress_line1address_line2cityprovincepostal_codecountrylatitudelongitudeis_activeis_primarycreated_bydeleted_bycreated_atupdated_atdeleted_atPaymentMethod
idorganization_idtypeAUTOMATIC is Xendit-integrated; MANUAL is bank transfer with proof.
providerbank_codebank_nameaccount_numberaccount_holder_nameis_activeis_primarycreated_atupdated_atXenditAccount
idorganization_iduser_emailinvitation_sent_atinvitation_accepted_atotp_expires_atotp_verified_atotp_attemptsis_registeredaccount_idbusiness_namestatuskyc_statuskyc_requested_atkyc_verified_atcreated_atupdated_atOrderItem
idproduct_idproduct_nameproduct_imagequantityunit_priceDecimal as string (preserves precision).
total_priceweightproduct_typeOrderPayment
idpayment_providerpayment_typeamountfee_amountnet_amountstatusreference_numberFor Xendit, the hosted invoice URL.
external_idexpires_atpaid_atconfirmed_atproof_urlfailure_reasonbank_codebank_nameaccount_numberaccount_holder_namecreated_atShippingAddress
recipient_namephoneaddress_line1address_line2cityprovincepostal_codecountrylatitudelongitudeOrderCourier
courier_codecourier_nameservice_typetracking_numberestimated_deliveryshipped_atdelivered_atOrder
idorganization_idorder_numberAuto-allocated MAX+1 within the organization.
subtotalshipping_costdiscountdiscount_typediscount_valuetotalorder_statuspayment_statusorigin_address_idnotesinternal_notesNot auto-historied. Internal only.
created_atupdated_atcancelled_atcancelled_reasonOrderHistoryRow
idorder_idevent_typeprevious_statusnew_statusdescriptionmetadatacreated_bycreated_by_usercreated_atOrderCreateRequest
customer_idExisting contact id. When set, server snapshots name / email / phone β raw fields override per-order.
customer_nameRequired when customer_id is not provided.
customer_emailcustomer_phoneRequired when customer_id is not provided.
customer_channelcourier_codecourier_namecourier_serviceshipping_costorigin_address_idpayment_method_idDrives the payment row + Xendit invoice creation when the method is AUTOMATIC+XENDIT.
notesdiscount_typediscount_valueOrderUpdateRequest
order_statusSide-effects: SHIPPING β shipped_at, COMPLETED β delivered_at, CANCELLED β cancelled_at + cancelled_by.
tracking_numberinternal_notesOrderInvoiceData
orderSlim invoice projection β drops internal_notes, metadata, and weight from the full Order shape.
templateWhen the org has not configured ecommerce_invoice_templates, returned as null with template_configured: false.
template_configuredPaymentConfirmRequest
payment_idMust reference a payment row whose parent order belongs to your organization.
BiteshipRateItem
nameweightGrams.
quantityvalueItem value in IDR.
descriptionCurrently not forwarded upstream β accepted for forward compatibility.
lengthwidthheightBiteshipRatesRequest
origin_postal_codedestination_postal_codeorigin_latitudeorigin_longitudedestination_latitudedestination_longitudecouriersDefault ["jne","jnt","sicepat","anteraja","ninja","lion","pos","tiki"] when omitted β same shortlist as the dashboard.
BiteshipRate
courier_codecourier_nameservice_codeservice_namedescriptiondurationpricetypeBiteshipRatesData
total_weightSum of items[].weight * items[].quantity.
Flat list sorted by price ASC.
Same set collated by courier β services sorted by price ASC, groups sorted by courier_name.
AssignmentData
conversation_idassignment_statusFull user profile.
AssignmentRequest
assigned_agent_idassigned_team_idreasonAIAgentToggleRequest
is_activetrue enables auto-reply, false pauses it.
reasonStatusTransitionRequest
statusreasonAISummaryData
message_idUUID of the persisted private AI_SUMMARY message. Empty string when is_cached=true (the prior message is reused).
message_countis_cachedtrue when the fingerprint matched and no new LLM call was made.
messageHuman-readable status line.
customer_intentkey_pointsnext_actionscreated_atAIQuickReplySuggestion
toneDrives the chip color in the composer.
textDrop-in body for message.send β language follows the conversation's recent context.
AIQuickReplyData
message_countHow many recent messages the LLM analyzed (default 40).
messagecreated_atMyPermissionsData
role_nameThe caller's role display name from roles.name. Empty string when the legacy users.role=OWNER shortcut is in effect.
is_ownerTrue when the caller bypasses all RBAC rules (legacy users.role=OWNER).
rbac_enabledServer-side RBAC enforcement flag. When false the middleware is in kill-switch mode β the UI should bypass guards as well.
Flat map keyed by resource_slug. Slugs missing from the map default to NO_ACCESS client-side.
CampaignDetail
idnamedescriptioncampaign_typestatusmessage_contentmessage_format_typemedia_urlmedia_typechannelaudience_typerecipient_listwhatsapp_templateFlat map keyed by Meta placeholder index ("1", "2", β¦) β either a literal value or a {{column_name}} reference into the picked recipient list / segment row.
scheduled_attimezonesent_atcompleted_attotal_recipientscreated_bycreated_atupdated_atcan_editcan_sendcan_cancelcan_deleteCampaignDetailEnvelope
Full broadcast record returned by every Campaigns mutator and by GET /campaigns/{id}. Same shape across all three so the mobile client has one DTO.
CampaignActionResponse
idstatustotal_recipientssent_atcompleted_atCampaignCreateRequest
namemessage_contentchannel_idThe inbox to send from. Must be an active channel in the caller's org.
descriptioncampaign_typemessage_format_typemedia_urlSet to the URL returned by POST /campaigns/media when the broadcast carries an image / video / document.
media_typeaudience_typetarget_segmentsRequired when audience_type=SEGMENTS. UUIDs of contact_segments rows.
recipient_list_idRequired when audience_type=RECIPIENT_LIST.
whatsapp_template_idRequired when message_format_type=TEMPLATE. The template must be APPROVED.
Flat map keyed by Meta placeholder index ("1", "2", β¦).
statusscheduled_atRequired when status=SCHEDULED. Must be in the future.
timezonesend_nowAtomic create + send shortcut. When true: status is forced to DRAFT, scheduled_at is forced to null, and the SendNow pipeline runs synchronously after persistence.
CampaignUpdateRequest
namedescriptioncampaign_typemessage_contentmessage_format_typemedia_urlmedia_typechannel_idaudience_typetarget_segmentsrecipient_list_idwhatsapp_template_idstatusscheduled_attimezonesend_nowSame shortcut as on Create. When true the patch forces DRAFT + scheduled_at=null then runs SendNow.
CampaignDuplicateRequest
new_nameCampaignSendRequest
send_atCampaignRecipientItem
idcontact_idcontact_namephone_numberstatussent_atdelivered_atread_atfailed_atfailed_reasonerror_codeerror_titleerror_messageexternal_message_idcreated_atCampaignMetricsSummary
total_recipientspending_countsent_countdelivered_countread_countfailed_countdelivery_rateopen_rateCampaignChannelMetric
channel_idchannel_namechannel_typetotal_targetedsent_countdelivered_countread_countfailed_countpending_countdelivery_rateopen_rateCampaignMediaUploadData
urlmedia_typemime_typefile_namefile_sizefile_size_formattedWhatsAppTemplateDetail
idorganization_idchannel_idwaba_template_idMeta-side template id. Populated once the template has been submitted and Meta has acknowledged the create call.
namelanguagecategorystatusrejection_reasonquality_scoreRaw Meta components[] JSON.
header_media_handleMeta media handle returned by POST /whatsapp-templates/media. Required when the template has a HEADER component with format IMAGE / VIDEO / DOCUMENT.
message_send_ttl_secondsAUTHENTICATION templates only β OTP delivery window.
submitted_atapproved_atlast_synced_atcreated_atupdated_atchannelWhatsAppTemplateDetailEnvelope
Full WhatsApp template row. Same shape as a list item plus richer timestamps. Components are passed through verbatim from the row's jsonb column so the mobile renderer can preview the template byte-for-byte the way the dashboard does.
WhatsAppTemplateComponentInput
typeformattextexamplesWhatsAppTemplateCreateRequest
channel_idWhatsApp channel that owns this template. Must be an active WHATSAPP_OFFICIAL inbox.
nameWill be lower-cased + trimmed to Meta's [a-z0-9_] rule before persistence.
languagecategoryAUTHENTICATION templates are not creatable via the public API in v1.
header_media_handlemessage_send_ttl_secondsWhatsAppTemplateUpdateRequest
header_media_handlemessage_send_ttl_secondsWhatsAppTemplateSyncRequest
channel_idWhatsApp channel whose Meta catalogue to sync.
WhatsAppTemplateSyncData
total_from_metacreatedupdatedskippedsynced_atWhatsAppTemplateMediaUploadData
handleMeta media handle. Drop into header_media_handle on the next create / update payload.
TicketMarkDoneRequest
is_doneSendMessageRecipient
typeDefaults to phone when phone is set, else external_id.
phoneFull international number β country code included, no leading 0 (e.g. 628123456789). Used for WhatsApp. Do NOT send a separate country_code.
valuePlatform identifier for username / external_id recipients (Instagram / Facebook / Telegram user id or handle).
nameOptional display name β used only when the contact is brand new.
SendMessageMedia
file_idUploaded media id from POST /uploads.
urlPublic URL passed straight to the provider.
captionOptional caption.
filenameFile name (documents).
SendMessageTemplate
nameApproved template name. Find it via GET /whatsapp-templates.
languageTemplate language code.
Body placeholder values, keyed "1", "2", β¦ for {{1}}, {{2}}.
media_urlHeader media override when the template header format is IMAGE/VIDEO/DOCUMENT.
SendMessageButton
titleButton label.
idPayload echoed back when the user taps the button. Auto-generated if omitted.
SendMessageInteractive
bodyPrompt text (falls back to top-level text).
SendMessageRequest
channel_idThe inbox/number to send from. Obtain it from GET /inboxes or GET /channels/by-identifier.
typeMessage kind. FILE is an alias for DOCUMENT.
conversation_idOptional. Target an existing conversation directly. When set, to is ignored; when omitted, to is required and the conversation is resolved-or-created.
Recipient addressing. Required unless conversation_id is set. Exactly one identifier is used, chosen by type.
textBody for TEXT (also the interactive body fallback).
Media reference for IMAGE / VIDEO / DOCUMENT / AUDIO / FILE. Provide file_id (from POST /uploads) OR a public url.
WhatsApp Official template payload (TEMPLATE type). The approved template is re-resolved server-side by (name + language + channel); variables fill the {{n}} body placeholders.
Reply buttons (INTERACTIVE type). Native on WhatsApp Official (max 3) and Instagram (max 13); on WhatsApp Unofficial it degrades to a numbered-text list.
reply_to_message_idOptional. Native quoted reply β the internal message UUID or the channel-side external id of the parent.
client_referenceOptional partner correlation id, echoed back in the response and stored on the message.
SendMessageData
message_idconversation_idstatusexternal_message_idProvider message id (e.g. WhatsApp wamid).
typesent_atclient_referenceSendMessageErrorEnvelope
successcodemessageChannelByIdentifierData
idThe channel_id to pass into POST /messages.
nametypestatusidentifierThe canonical memorable identifier for this channel type (phone / @username / page / widget id).
phone_numberWhatsApp Official/Unofficial.
usernameInstagram / Facebook page / WhatsApp Official display phone.
account_namebot_usernameTelegram.
widget_idWebchat.
external_idPlatform id (WABA id / IG id / FB page id).
