Send a reply with a key that has the send scope. The Idempotency-Key makes a retry safe: a retry with the same key returns the first result and does not send again.
Send a reply
IDEMPOTENCY_KEY=$(uuidgen) # make it once; reuse it on a retry
curl -X POST https://app.teamchirp.io/v1/conversations/1042/replies \
-H "Authorization: Bearer $CHIRP_API_KEY" \
-H "Idempotency-Key: $IDEMPOTENCY_KEY" \
-H "Content-Type: application/json" \
-d '{"body":"Thanks, Dana. The refund is on its way."}'
Authentication
Create a key in Chirp under Settings → API keys. Chirp shows the secret once; it starts with chirp_. Send it in the Authorization header of every request:
Authorization: Bearer chirp_…
A key acts as the user who created it: it has that user's role and sees only the inboxes that user can see. A conversation the user cannot see answers 404, as if it did not exist. A key stops working when it is revoked or expires, or when its user is disabled or leaves the workspace.
Scopes
A key holds one or more scopes. Each operation names the scope it needs in x-required-scope.
read: list inboxes and conversations, and read a conversation.
write: change the status or assignee, add and remove tags, and add notes.
send: send replies and start conversations.
A call without the scope answers 403 with the code insufficient_scope. A Viewer's key keeps only read, whatever scopes it was made with.
Rate limits
Each key may make a fixed number of requests per minute. Every authenticated response carries X-RateLimit-Limit and X-RateLimit-Remaining. When the limit is used up, the API answers 429 with a Retry-After header in seconds.
Pagination
GET /conversations returns one page and a nextCursor. Pass it as cursor to get the next page; it is null on the last page.
Idempotency
The send operations and adding a note accept an Idempotency-Key header holding a UUID. A retry with the same key returns the first result instead of sending or adding again.
Conflicts
Each conversation has a version that goes up with each change. Send it back as expectedVersion to have a change refused with 409 if someone changed the conversation first.
Errors
Every error has the same body: {"error": {"code": "not_found", "message": "Conversation not found"}}. Branch on code; the message is for people. The codes are listed in the Error schema.
Conversations in the inboxes the key's user can see, newest activity first, in every status unless you filter.
Filters use the app's search syntax: pass tokens in q (such as status:open tag:"billing issue") or as parameters. Different filters must all match; a repeated status, assignee, inbox or linked matches any of its values, and a repeated tag needs every tag. q and the filters together may hold at most 200 characters.
The result is one page. Pass nextCursor as cursor to get the next one. A conversation that gets new activity during a walk moves to the front, so a walk that started earlier does not return it again.
Parameters
qquery · string
Search text, matched in addresses, subjects, bodies and notes; may hold filter tokens.
statusquery · array of string
open, pending, snoozed or closed.
assigneequery · array of string
me, none or a teammate's display name.
tagquery · array of string
A tag name; every tag given must be on the conversation.
inboxquery · array of string
An inbox id, name, address or local part.
afterquery · string
Latest activity on or after this day, in the user's time zone: YYYY-MM-DD, today, a weekday such as mon, lastweek, 1d–365d days back, or a month day such as sep15.
beforequery · string
Latest activity before this day, in the same forms as after.
linkedquery · array of string
open, done, any, none or a Linear issue identifier.
isquery · string
attention: the Needs attention list (open and waiting on you, or with a delivery problem).
Change the status or the assignee: one of the two per call. To snooze, send status snoozed with snoozedUntil: a future time, or null to wait for the customer's reply. The change shows in the timeline as the key's user.
Parameters
idpath · stringrequired
The conversation's id.
Request body
statusstring
The new status. One of open, pending, snoozed, closed.
assigneeIdstring | null
The teammate to assign, or null to unassign. The teammate must be able to see the inbox.
snoozedUntilstring (date-time) | null
Required with status snoozed: when to wake it, or null to wait for the customer's reply.
expectedVersioninteger
Refuse the change with 409 if the conversation's version is no longer this.
Start a new conversation with one message, sent now from the inbox with its From name and signature. The app's undo window does not apply. Send an Idempotency-Key so a retry cannot send twice.
A 201 means Chirp recorded the message, not that it was delivered. Check state: only sent means the mail provider accepted it. On failed the provider refused it; on unknown, read the conversation before you send again.
Parameters
Idempotency-Keyheader · string (uuid)
A UUID. A retry with the same key returns the first result instead of doing the work again.
Request body
inboxIdintegerrequired
The inbox to send from. The key's user must be able to see it.
tostring (email)required
One recipient address.
subjectstringrequired
The subject, 1–200 characters.
bodystringrequired
The plain text, 1–10000 characters. The inbox signature is added.
A field is missing, unknown or not valid, or Idempotency-Key is not a UUID.
401unauthorized
The API key is missing, invalid, revoked or expired.
403insufficient_scope
The key lacks the scope this operation needs.
404not_found
No such inbox, or the key's user cannot see it.
409conflict
The inbox cannot send mail right now; its connection needs attention in Chirp.
413payload_too_large
The JSON body exceeds 64 KB.
429rate_limited
The key used up its requests for this minute.
500internal_error
The server failed to complete the request.
Request
curl -X POST https://app.teamchirp.io/v1/conversations \
-H "Authorization: Bearer $CHIRP_API_KEY" \
-H "Idempotency-Key: $IDEMPOTENCY_KEY" \
-H "Content-Type: application/json" \
-d '{"inboxId":1,"to":"[email protected]","subject":"Your refund for order 5521","body":"Hi Dana, we have refunded the second charge."}'
Send a reply to the customer now, from the conversation's inbox with its From name and signature. The app's undo window does not apply. Send an Idempotency-Key so a retry cannot send twice.
A 201 means Chirp recorded the message, not that it was delivered. Check state: only sent means the mail provider accepted it. On failed the provider refused it; on unknown, read the conversation before you send again.
Parameters
idpath · stringrequired
The conversation's id.
Idempotency-Keyheader · string (uuid)
A UUID. A retry with the same key returns the first result instead of doing the work again.
Request body
bodystringrequired
The reply's plain text, 1–10000 characters. The inbox signature is added.
The body is empty or too long, a field is unknown, or Idempotency-Key is not a UUID.
401unauthorized
The API key is missing, invalid, revoked or expired.
403insufficient_scope
The key lacks the scope this operation needs.
404not_found
No such conversation, or the key's user cannot see its inbox.
413payload_too_large
The JSON body exceeds 64 KB. Also sent when the finished message exceeds 10 MB.
429rate_limited
The key used up its requests for this minute.
500internal_error
The server failed to complete the request.
Request
curl -X POST https://app.teamchirp.io/v1/conversations/1042/replies \
-H "Authorization: Bearer $CHIRP_API_KEY" \
-H "Idempotency-Key: $IDEMPOTENCY_KEY" \
-H "Content-Type: application/json" \
-d '{"body":"Sorry about that, Dana. We have refunded the second charge."}'
A machine-readable code. Branch on this, not on the message. One of invalid_request, unauthorized, forbidden, not_found, conflict, payload_too_large, rate_limited, upstream_error, insufficient_scope, internal_error.
incoming from the customer, or sent from the inbox. One of incoming, sent.
fromstringrequired
The From address.
tostringrequired
The To address.
subjectstringrequired
The subject.
bodystringrequired
The plain-text body.
createdAtstring (date-time)required
When it arrived or was sent.
authorstring | nullrequired
The id of the teammate who sent it, or null for an incoming message.
attachmentsarray of objectrequired
Its attachments. The API does not serve their content.
attachments[].filenamestringrequired
The file name.
attachments[].contentTypestringrequired
The MIME type.
attachments[].sizeintegerrequired
The size in bytes.
statestring | nullrequired
For a sent message, its outbox state: held (in the app's undo window), sending, sent, failed or unknown. Null for an incoming message. One of held, sending, sent, failed, unknown.
One entry in a conversation's timeline: a note or a change.
idstringrequired
The entry's id.
kindstringrequired
What happened. A note carries its text in body; a change carries its data in detail. One of note, status, assignment, tag_add, tag_remove, reopened, issue_link, issue_unlink, issue_state, automation.
bodystring | nullrequired
A note's text; null for other kinds.
detailobjectrequired
The change's data, which depends on kind (for example from and to for status). Empty for a note.
actorobject | nullrequired
Who made it, or null for the system (an automation or an incoming message).
createdAtstring (date-time)required
When it happened.
parentNoteIdstring | nullrequired
For a reply to a note, the note's id; otherwise null.
The outcome of a send. The response is 201 whenever Chirp recorded the message, even when delivery failed: check state.
conversationIdstringrequired
The conversation's id.
messageIdstringrequired
The sent message's id.
statestringrequired
sent: the mail provider accepted the message. failed: the provider refused it. unknown: the outcome is not known, for example after a timeout; check the conversation before you send again. sending: a retry with the same Idempotency-Key arrived while the first send was still running. One of sending, sent, failed, unknown.