Inbox API
Every Inbox action (63) and data model (22): endpoints, scopes, input, preconditions, events and examples.
Every channel in one inbox — conversations, replies, teams and who answers.
Module code inbox · 63 actions · 22 data models · 45 events. A key or token never does more here than the person behind it can; row-level security applies exactly as in the app. Scopes: inbox (everything), inbox:read (read only) or inbox:<capability>.
Resources
| Resource | Table | List and read | POST | PATCH | DELETE |
|---|---|---|---|---|---|
agent_bot_inboxes | agent_bot_inbox | /api/v1/agent_bot_inboxes | |||
agent_bots | agent_bot | /api/v1/agent_bots | inbox.save_agent_bot | inbox.delete_agent_bot | |
applied_slas | applied_sla | /api/v1/applied_slas | |||
assignment_policies | assignment_policy | /api/v1/assignment_policies | inbox.save_assignment_policy | ||
canned_responses | canned_response | /api/v1/canned_responses | inbox.save_canned_response | inbox.delete_canned_response | |
conversation_mentions | conversation_mention | /api/v1/conversation_mentions | |||
conversation_participants | conversation_participant | /api/v1/conversation_participants | |||
conversations | conversation | /api/v1/conversations | inbox.open_conversation | ||
csat_survey_responses | csat_survey_response | /api/v1/csat_survey_responses | |||
inbox_member_preferences | inbox_member_preference | /api/v1/inbox_member_preferences | |||
inbox_members | inbox_member | /api/v1/inbox_members | |||
inbox_working_hours | inbox_working_hour | /api/v1/inbox_working_hours | |||
inboxes | inbox | /api/v1/inboxes | inbox.create_inbox | inbox.update_inbox | |
macros | macro | /api/v1/macros | inbox.save_macro | inbox.delete_macro | |
message_attachments | message_attachment | /api/v1/message_attachments | |||
messages | message | /api/v1/messages | inbox.delete_message | ||
reporting_events | reporting_event | /api/v1/reporting_events | |||
sla_events | sla_event | /api/v1/sla_events | |||
sla_policies | sla_policy | /api/v1/sla_policies | inbox.save_sla_policy | inbox.delete_sla_policy | |
team_members | team_member | /api/v1/team_members | |||
teams | team | /api/v1/teams | inbox.save_team | inbox.delete_team | |
whatsapp_message_templates | whatsapp_message_template | /api/v1/whatsapp_message_templates |
Lists take limit (up to 200), cursor, order (e.g. -created_at), fields and q; every other query parameter is a column filter: ?status=paid, ?total[gte]=100, ?id[in]=a,b, ?x=null.
Actions
| Action | Needs | What it does |
|---|---|---|
inbox.add_participant | inbox:write | Add a follower |
inbox.add_private_note | inbox:comment | Add a private note |
inbox.apply_sla | inbox:write | Apply an SLA |
inbox.assign | inbox:write | Assign a conversation |
inbox.assign_bot | inbox:write | Hand a conversation to a bot |
inbox.assign_era | inbox:write | Give a conversation to Era |
inbox.assign_team | inbox:write | Move a conversation to a team |
inbox.auto_assign | inbox:write | Assign automatically |
inbox.bot_reply | inbox:handle | A bot replies |
inbox.bot_set_status | inbox:handle | A bot hands over or resolves |
inbox.bulk_update | inbox:write | Change several conversations |
inbox.change_customer | inbox:write | Move a conversation to another person |
inbox.create_inbox | inbox:manage | Connect a channel |
inbox.deactivate_inbox | inbox:manage | Take a channel out of service |
inbox.delete_agent_bot | inbox:manage | Remove a bot |
inbox.delete_canned_response | inbox:manage | Delete a saved reply |
inbox.delete_macro | inbox:write | Delete a macro |
inbox.delete_message | inbox:delete | Delete a message |
inbox.delete_sla_policy | inbox:manage | Delete an SLA policy |
inbox.delete_team | inbox:manage | Delete a team |
inbox.edit_message | inbox:write | Edit a message |
inbox.evaluate_sla | inbox:handle | Check an SLA |
inbox.ingest | inbox:handle | Take in an arriving message |
inbox.mark_read | inbox:read | Mark read |
inbox.mark_unread | inbox:read | Mark unread |
inbox.mute | inbox:write | Mute |
inbox.open_conversation | inbox:write | Open a conversation |
inbox.record_bot_delivery | inbox:handle | Record a bot delivery |
inbox.record_channel_health | inbox:handle | Record a connection test |
inbox.record_delivery | inbox:handle | Record what a channel said |
inbox.record_poll_state | inbox:handle | Record the mailbox cursor |
inbox.record_visitor_seen | inbox:handle | Record that the visitor saw the thread |
inbox.remove_participant | inbox:write | Remove a follower |
inbox.reopen | inbox:write | Reopen a conversation |
inbox.reply | inbox:write | Reply |
inbox.reset_agent_bot_credentials | inbox:manage | Reset a bot’s token or secret |
inbox.resolve | inbox:write | Resolve a conversation |
inbox.retry_delivery | inbox:write | Retry sending |
inbox.review_csat | inbox:write | Review a rating |
inbox.run_macro | inbox:write | Run a macro |
inbox.save_agent_bot | inbox:manage | Register a bot |
inbox.save_assignment_policy | inbox:manage | Save a routing rule |
inbox.save_canned_response | inbox:manage | Save a reply |
inbox.save_macro | inbox:write | Save a macro |
inbox.save_reply_from_message | inbox:manage | Save this as a reply |
inbox.save_sla_policy | inbox:manage | Save an SLA policy |
inbox.save_team | inbox:manage | Save a team |
inbox.send_transcript | inbox:write | Email the transcript |
inbox.set_auto_resolve | inbox:manage | Set automatic resolution |
inbox.set_inbox_bot | inbox:manage | Choose the bot on an inbox |
inbox.set_inbox_members | inbox:manage | Set who works an inbox |
inbox.set_inbox_policy | inbox:manage | Set an inbox’s routing rule |
inbox.set_mailbox | inbox:manage | Sign a mailbox in with Gmail or Outlook |
inbox.set_member_preference | inbox:read | Save Inbox preferences |
inbox.set_priority | inbox:write | Set priority |
inbox.set_team_members | inbox:manage | Set a team’s members |
inbox.set_working_hours | inbox:manage | Set business hours |
inbox.snooze | inbox:write | Snooze a conversation |
inbox.submit_csat | inbox:handle | Take a rating |
inbox.sync_whatsapp_templates | inbox:manage | Sync WhatsApp templates |
inbox.translate_message | inbox:write | Translate a message |
inbox.unmute | inbox:write | Unmute |
inbox.update_inbox | inbox:manage | Edit an inbox |
inbox.add_participant
Add a follower — Lets somebody follow a conversation without being assigned it.
Needs inbox:write · changes data
https://app.getera.io/api/v1/conversation_participants/actions/add_participant- Canonical address:
POST /api/v1/actions/inbox.add_participant
Input
| Field | Type | Required | Notes |
|---|---|---|---|
id | uuid | yes | Conversation · id of conversation |
userId | uuid | yes | Person |
Preconditions
The action is refused with 409 and one of these codes:
| Code | Refused when |
|---|---|
conversation_not_found | The conversation must exist. |
not_a_member | They must be an active member of the workspace. |
already_participating | They must not already be following it. |
Events it publishes: inbox.participant_added
What it writes: conversation_participant (insert) — Adds the follower
curl -X POST https://app.getera.io/api/v1/conversation_participants/actions/add_participant \
-H "Authorization: Bearer $ERA_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{"id":"CONVERSATION_ID","userId":"USER_ID"}'{
"ok": true,
"result": "…",
"events": [
"inbox.participant_added"
]
}import { createEra } from "@getera/sdk"
const era = createEra({ apiKey: process.env.ERA_API_KEY })
const res = await era.conversationParticipants.addParticipant({
id: "CONVERSATION_ID",
userId: "USER_ID"
})era conversation_participants add_participant \
--id "CONVERSATION_ID" \
--user-id "USER_ID"{
"name": "era__inbox__add_participant",
"arguments": {
"id": "CONVERSATION_ID",
"userId": "USER_ID"
}
}Errors: 401 no or expired credential · 403 the credential does not hold inbox:write · 404 the record or module is outside what it may reach · 422 invalid input (problems[]) · 409 conversation_not_found, not_a_member, already_participating · 429 slow down
inbox.add_private_note
Add a private note — Leaves a note on the conversation that the customer never sees.
Needs inbox:comment · changes data
https://app.getera.io/api/v1/conversations/{id}/actions/add_private_note- Canonical address:
POST /api/v1/actions/inbox.add_private_note
Input
| Field | Type | Required | Notes |
|---|---|---|---|
conversationId | uuid | yes | Filled from the path {id} · Conversation · id of conversation |
content | string | Message · max 150000 | |
contentType | string (enum) | Kind · Allowed values: text, input_text, input_textarea, input_email, input_select, cards, form, article, incoming_email, input_csat, integrations, sticker, voice_call | |
attachments | object / array | Attachments | |
cannedResponseId | uuid | Saved reply · id of canned_response | |
templateId | uuid | WhatsApp template · id of whatsapp_message_template | |
templateVariables | object / array | Template values | |
cc | object / array | Cc · Email only: more addresses that get the reply. | |
bcc | object / array | Bcc · Email only: addresses that get the reply unseen. | |
replyToMessageId | uuid | In reply to · id of message |
Preconditions
The action is refused with 409 and one of these codes:
| Code | Refused when |
|---|---|
conversation_not_found | The conversation must exist. |
empty_message | A message needs words or an attachment. |
too_many_attachments | A message carries at most fifteen attachments. |
too_many_messages | Too many messages on one conversation in a minute. |
canned_response_not_found | The saved reply must still exist. |
mentioned_cannot_read | Everyone mentioned works the inbox or manages it, so they can open the conversation. |
Events it publishes: inbox.note_added, inbox.mentioned, inbox.user_mentioned
What it writes: message (insert) — Writes the private note; message_attachment (insert) — Attaches files; conversation (update) — Touches the activity clock; conversation_mention (upsert) — Records who was mentioned; conversation_participant (insert) — Makes the people mentioned followers
curl -X POST https://app.getera.io/api/v1/conversations/CONVERSATION_ID/actions/add_private_note \
-H "Authorization: Bearer $ERA_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{"content":"Message"}'{
"ok": true,
"result": "…",
"events": [
"inbox.note_added",
"inbox.mentioned",
"inbox.user_mentioned"
]
}import { createEra } from "@getera/sdk"
const era = createEra({ apiKey: process.env.ERA_API_KEY })
const res = await era.conversations.addPrivateNote("CONVERSATION_ID", {
content: "Message"
})era conversations add_private_note CONVERSATION_ID \
--content "Message"{
"name": "era__inbox__add_private_note",
"arguments": {
"conversationId": "CONVERSATION_ID",
"content": "Message"
}
}Errors: 401 no or expired credential · 403 the credential does not hold inbox:comment · 404 the record or module is outside what it may reach · 422 invalid input (problems[]) · 409 conversation_not_found, empty_message, too_many_attachments, too_many_messages, canned_response_not_found, mentioned_cannot_read · 429 slow down
inbox.apply_sla
Apply an SLA — Measures a conversation against a response-time policy.
Needs inbox:write · changes data
https://app.getera.io/api/v1/applied_slas/actions/apply_sla- Canonical address:
POST /api/v1/actions/inbox.apply_sla
Input
| Field | Type | Required | Notes |
|---|---|---|---|
conversationId | uuid | yes | Conversation · id of conversation |
policyId | uuid | yes | Policy · id of sla_policy |
Preconditions
The action is refused with 409 and one of these codes:
| Code | Refused when |
|---|---|
conversation_not_found | The conversation must exist. |
sla_policy_not_found | The policy must exist. |
sla_already_applied | A conversation keeps the first SLA it was given (the source’s rule). |
Events it publishes: inbox.sla_applied, inbox.sla_missed
What it writes: applied_sla (insert) — Starts measuring; sla_event (insert) — Records a target already missed
curl -X POST https://app.getera.io/api/v1/applied_slas/actions/apply_sla \
-H "Authorization: Bearer $ERA_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{"conversationId":"CONVERSATION_ID","policyId":"SLA_POLICY_ID"}'{
"ok": true,
"result": "…",
"events": [
"inbox.sla_applied",
"inbox.sla_missed"
]
}import { createEra } from "@getera/sdk"
const era = createEra({ apiKey: process.env.ERA_API_KEY })
const res = await era.appliedSlas.applySla({
conversationId: "CONVERSATION_ID",
policyId: "SLA_POLICY_ID"
})era applied_slas apply_sla \
--conversation-id "CONVERSATION_ID" \
--policy-id "SLA_POLICY_ID"{
"name": "era__inbox__apply_sla",
"arguments": {
"conversationId": "CONVERSATION_ID",
"policyId": "SLA_POLICY_ID"
}
}Errors: 401 no or expired credential · 403 the credential does not hold inbox:write · 404 the record or module is outside what it may reach · 422 invalid input (problems[]) · 409 conversation_not_found, sla_policy_not_found, sla_already_applied · 429 slow down
inbox.assign
Assign a conversation — Gives the conversation to somebody who works that inbox, or returns it to the queue.
Needs inbox:write · changes data
https://app.getera.io/api/v1/conversations/{id}/actions/assign- Canonical address:
POST /api/v1/actions/inbox.assign
Input
| Field | Type | Required | Notes |
|---|---|---|---|
id | uuid | yes | Filled from the path {id} · Conversation · id of conversation |
userId | uuid | Agent |
Preconditions
The action is refused with 409 and one of these codes:
| Code | Refused when |
|---|---|
conversation_not_found | The conversation must exist. |
not_a_member | The agent must be an active member of the workspace. |
not_an_inbox_member | The agent must work that inbox. |
not_in_conversation_team | An agent outside the conversation’s team cannot be given it. |
Events it publishes: inbox.assigned
What it writes: conversation (update) — Sets the assignee
curl -X POST https://app.getera.io/api/v1/conversations/CONVERSATION_ID/actions/assign \
-H "Authorization: Bearer $ERA_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{"userId":"USER_ID"}'{
"ok": true,
"result": "…",
"events": [
"inbox.assigned"
]
}import { createEra } from "@getera/sdk"
const era = createEra({ apiKey: process.env.ERA_API_KEY })
const res = await era.conversations.assign("CONVERSATION_ID", {
userId: "USER_ID"
})era conversations assign CONVERSATION_ID \
--user-id "USER_ID"{
"name": "era__inbox__assign",
"arguments": {
"id": "CONVERSATION_ID",
"userId": "USER_ID"
}
}Errors: 401 no or expired credential · 403 the credential does not hold inbox:write · 404 the record or module is outside what it may reach · 422 invalid input (problems[]) · 409 conversation_not_found, not_a_member, not_an_inbox_member, not_in_conversation_team · 429 slow down
inbox.assign_bot
Hand a conversation to a bot — Gives the conversation to a bot connected to that inbox, or takes it back.
Needs inbox:write · changes data
https://app.getera.io/api/v1/conversations/{id}/actions/assign_bot- Canonical address:
POST /api/v1/actions/inbox.assign_bot
Input
| Field | Type | Required | Notes |
|---|---|---|---|
id | uuid | yes | Filled from the path {id} · Conversation · id of conversation |
agentBotId | uuid | Bot · id of agent_bot |
Preconditions
The action is refused with 409 and one of these codes:
| Code | Refused when |
|---|---|
conversation_not_found | The conversation must exist. |
bot_not_on_inbox | The bot must be connected to that inbox and active. |
Events it publishes: inbox.assigned, inbox.bot_handoff
What it writes: conversation (update) — Sets or clears the bot; reporting_event (insert) — Writes the report clock reading (sql/126)
curl -X POST https://app.getera.io/api/v1/conversations/CONVERSATION_ID/actions/assign_bot \
-H "Authorization: Bearer $ERA_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{"agentBotId":"AGENT_BOT_ID"}'{
"ok": true,
"result": "…",
"events": [
"inbox.assigned",
"inbox.bot_handoff"
]
}import { createEra } from "@getera/sdk"
const era = createEra({ apiKey: process.env.ERA_API_KEY })
const res = await era.conversations.assignBot("CONVERSATION_ID", {
agentBotId: "AGENT_BOT_ID"
})era conversations assign_bot CONVERSATION_ID \
--agent-bot-id "AGENT_BOT_ID"{
"name": "era__inbox__assign_bot",
"arguments": {
"id": "CONVERSATION_ID",
"agentBotId": "AGENT_BOT_ID"
}
}Errors: 401 no or expired credential · 403 the credential does not hold inbox:write · 404 the record or module is outside what it may reach · 422 invalid input (problems[]) · 409 conversation_not_found, bot_not_on_inbox · 429 slow down
inbox.assign_era
Give a conversation to Era — Era drafts every reply as a private note, or answers — a reply still waits for your approval unless you allowed it.
Needs inbox:write · changes data
https://app.getera.io/api/v1/conversations/{id}/actions/assign_era- Canonical address:
POST /api/v1/actions/inbox.assign_era
Input
| Field | Type | Required | Notes |
|---|---|---|---|
id | uuid | yes | Filled from the path {id} · Conversation · id of conversation |
mode | string (enum) | yes | Era · Allowed values: draft, answer, off |
Preconditions
The action is refused with 409 and one of these codes:
| Code | Refused when |
|---|---|
conversation_not_found | The conversation must exist. |
era_off | Era (the AI module) must be on in the workspace. |
Events it publishes: inbox.era_assigned
What it writes: conversation (update) — Gives the conversation to Era, or takes it back
curl -X POST https://app.getera.io/api/v1/conversations/CONVERSATION_ID/actions/assign_era \
-H "Authorization: Bearer $ERA_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{"mode":"draft"}'{
"ok": true,
"result": "…",
"events": [
"inbox.era_assigned"
]
}import { createEra } from "@getera/sdk"
const era = createEra({ apiKey: process.env.ERA_API_KEY })
const res = await era.conversations.assignEra("CONVERSATION_ID", {
mode: "draft"
})era conversations assign_era CONVERSATION_ID \
--mode "draft"{
"name": "era__inbox__assign_era",
"arguments": {
"id": "CONVERSATION_ID",
"mode": "draft"
}
}Errors: 401 no or expired credential · 403 the credential does not hold inbox:write · 404 the record or module is outside what it may reach · 422 invalid input (problems[]) · 409 conversation_not_found, era_off · 429 slow down
inbox.assign_team
Move a conversation to a team — Routes the conversation to a team, dropping an assignee who is not in it.
Needs inbox:write · changes data
https://app.getera.io/api/v1/conversations/{id}/actions/assign_team- Canonical address:
POST /api/v1/actions/inbox.assign_team
Input
| Field | Type | Required | Notes |
|---|---|---|---|
id | uuid | yes | Filled from the path {id} · Conversation · id of conversation |
teamId | uuid | Team · id of team |
Preconditions
The action is refused with 409 and one of these codes:
| Code | Refused when |
|---|---|
conversation_not_found | The conversation must exist. |
team_not_found | The team must exist. |
Events it publishes: inbox.team_assigned
What it writes: conversation (update) — Sets the team
curl -X POST https://app.getera.io/api/v1/conversations/CONVERSATION_ID/actions/assign_team \
-H "Authorization: Bearer $ERA_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{"teamId":"TEAM_ID"}'{
"ok": true,
"result": "…",
"events": [
"inbox.team_assigned"
]
}import { createEra } from "@getera/sdk"
const era = createEra({ apiKey: process.env.ERA_API_KEY })
const res = await era.conversations.assignTeam("CONVERSATION_ID", {
teamId: "TEAM_ID"
})era conversations assign_team CONVERSATION_ID \
--team-id "TEAM_ID"{
"name": "era__inbox__assign_team",
"arguments": {
"id": "CONVERSATION_ID",
"teamId": "TEAM_ID"
}
}Errors: 401 no or expired credential · 403 the credential does not hold inbox:write · 404 the record or module is outside what it may reach · 422 invalid input (problems[]) · 409 conversation_not_found, team_not_found · 429 slow down
inbox.auto_assign
Assign automatically — Picks the next agent by the inbox’s routing rule, within their fair share.
Needs inbox:write · changes data
https://app.getera.io/api/v1/conversations/{id}/actions/auto_assign- Canonical address:
POST /api/v1/actions/inbox.auto_assign
Input
| Field | Type | Required | Notes |
|---|---|---|---|
id | uuid | yes | Filled from the path {id} · Conversation · id of conversation |
Preconditions
The action is refused with 409 and one of these codes:
| Code | Refused when |
|---|---|
conversation_not_found | The conversation must exist. |
inbox_not_found | The inbox must exist. |
auto_assignment_off | Auto-assignment must be switched on for the inbox. |
policy_disabled | The inbox’s routing rule must be enabled. |
Events it publishes: inbox.assigned
What it writes: conversation (update) — Sets the assignee; inbox_member (update) — Moves the agent to the back of the queue
curl -X POST https://app.getera.io/api/v1/conversations/CONVERSATION_ID/actions/auto_assign \
-H "Authorization: Bearer $ERA_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{}'{
"ok": true,
"result": "…",
"events": [
"inbox.assigned"
]
}import { createEra } from "@getera/sdk"
const era = createEra({ apiKey: process.env.ERA_API_KEY })
const res = await era.conversations.autoAssign("CONVERSATION_ID")era conversations auto_assign CONVERSATION_ID{
"name": "era__inbox__auto_assign",
"arguments": {
"id": "CONVERSATION_ID"
}
}Errors: 401 no or expired credential · 403 the credential does not hold inbox:write · 404 the record or module is outside what it may reach · 422 invalid input (problems[]) · 409 conversation_not_found, inbox_not_found, auto_assignment_off, policy_disabled · 429 slow down
inbox.bot_reply
A bot replies — What a bot says to the customer, through the bot API.
Needs inbox:handle · reaches outside (sends, publishes or charges)
https://app.getera.io/api/v1/conversations/{id}/actions/bot_reply- Canonical address:
POST /api/v1/actions/inbox.bot_reply
Input
| Field | Type | Required | Notes |
|---|---|---|---|
agentBotId | uuid | yes | Bot · id of agent_bot |
conversationId | uuid | yes | Filled from the path {id} · Conversation · id of conversation |
content | string | Message · max 150000 | |
contentType | string | Kind of message · max 40 | |
contentAttributes | object / array | Options, cards or form |
Preconditions
The action is refused with 409 and one of these codes:
| Code | Refused when |
|---|---|
agent_bot_not_found | The bot must exist. |
conversation_not_found | The conversation must exist. |
bot_not_on_inbox | The bot holds the conversation or answers on its inbox. |
unsupported_content_type | A bot sends text, options, cards, forms, articles or input requests. |
empty_message | A text message needs words. |
conversation_resolved | A resolved conversation only takes messages where the inbox allows it. |
too_many_messages | The per-minute flood limit applies to bots too. |
Events it publishes: inbox.message_sent
What it writes: message (insert) — Writes the bot’s message, queued for delivery; conversation (update) — Clears the waiting clock
curl -X POST https://app.getera.io/api/v1/conversations/CONVERSATION_ID/actions/bot_reply \
-H "Authorization: Bearer $ERA_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{"agentBotId":"AGENT_BOT_ID"}'{
"ok": true,
"result": "…",
"events": [
"inbox.message_sent"
]
}import { createEra } from "@getera/sdk"
const era = createEra({ apiKey: process.env.ERA_API_KEY })
const res = await era.conversations.botReply("CONVERSATION_ID", {
agentBotId: "AGENT_BOT_ID"
})era conversations bot_reply CONVERSATION_ID \
--agent-bot-id "AGENT_BOT_ID"{
"name": "era__inbox__bot_reply",
"arguments": {
"conversationId": "CONVERSATION_ID",
"agentBotId": "AGENT_BOT_ID"
}
}Errors: 401 no or expired credential · 403 the credential does not hold inbox:handle · 404 the record or module is outside what it may reach · 422 invalid input (problems[]) · 409 agent_bot_not_found, conversation_not_found, bot_not_on_inbox, unsupported_content_type, empty_message, conversation_resolved, too_many_messages · 429 slow down
inbox.bot_set_status
A bot hands over or resolves — The bot passes the conversation to people, or closes it.
Needs inbox:handle · changes data
https://app.getera.io/api/v1/conversations/{id}/actions/bot_set_status- Canonical address:
POST /api/v1/actions/inbox.bot_set_status
Input
| Field | Type | Required | Notes |
|---|---|---|---|
agentBotId | uuid | yes | Bot · id of agent_bot |
conversationId | uuid | yes | Filled from the path {id} · Conversation · id of conversation |
status | string (enum) | yes | Status · Allowed values: open, resolved |
reason | string | Why · max 40 |
Preconditions
The action is refused with 409 and one of these codes:
| Code | Refused when |
|---|---|
agent_bot_not_found | The bot must exist. |
conversation_not_found | The conversation must exist. |
bot_not_on_inbox | The bot holds the conversation or answers on its inbox. |
already_open | A conversation already with people is not handed over again. |
already_resolved | A resolved conversation is not resolved twice. |
Events it publishes: inbox.bot_handoff, inbox.resolved, inbox.message_sent
What it writes: conversation (update) — Opens it for people, or resolves it; message (insert) — The rating request, when resolving asks for one; reporting_event (insert) — Writes the report clock reading (sql/126)
curl -X POST https://app.getera.io/api/v1/conversations/CONVERSATION_ID/actions/bot_set_status \
-H "Authorization: Bearer $ERA_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{"agentBotId":"AGENT_BOT_ID","status":"open"}'{
"ok": true,
"result": "…",
"events": [
"inbox.bot_handoff",
"inbox.resolved",
"inbox.message_sent"
]
}import { createEra } from "@getera/sdk"
const era = createEra({ apiKey: process.env.ERA_API_KEY })
const res = await era.conversations.botSetStatus("CONVERSATION_ID", {
agentBotId: "AGENT_BOT_ID",
status: "open"
})era conversations bot_set_status CONVERSATION_ID \
--agent-bot-id "AGENT_BOT_ID" \
--status "open"{
"name": "era__inbox__bot_set_status",
"arguments": {
"conversationId": "CONVERSATION_ID",
"agentBotId": "AGENT_BOT_ID",
"status": "open"
}
}Errors: 401 no or expired credential · 403 the credential does not hold inbox:handle · 404 the record or module is outside what it may reach · 422 invalid input (problems[]) · 409 agent_bot_not_found, conversation_not_found, bot_not_on_inbox, already_open, already_resolved · 429 slow down
inbox.bulk_update
Change several conversations — Resolves, snoozes, assigns, routes or ranks a selection at once — all or none.
Needs inbox:write · changes data
https://app.getera.io/api/v1/messages/actions/bulk_update- Canonical address:
POST /api/v1/actions/inbox.bulk_update
Input
| Field | Type | Required | Notes |
|---|---|---|---|
ids | object / array | yes | Conversations |
status | string (enum) | Status · Allowed values: open, resolved, pending, snoozed | |
snoozedUntil | date-time | Snooze until | |
assigneeUserId | uuid | Assign to | |
unassign | boolean | Return to the queue | |
teamId | uuid | Team · id of team | |
clearTeam | boolean | Take off the team | |
priority | string (enum) | Priority · Allowed values: low, medium, high, urgent, none |
Preconditions
The action is refused with 409 and one of these codes:
| Code | Refused when |
|---|---|
invalid_selection | Between one and a hundred conversations are chosen. |
nothing_to_change | Something to change is chosen. |
Events it publishes: inbox.assigned, inbox.team_assigned, inbox.resolved, inbox.reopened, inbox.snoozed, inbox.priority_changed, inbox.message_sent
What it writes: conversation (update) — Applies the change to each conversation; inbox_member (update) — Moves an assignee to the back of the queue; message (insert) — A rating request, when resolving asks for one; reporting_event (insert) — Writes the report clock reading (sql/126)
curl -X POST https://app.getera.io/api/v1/messages/actions/bulk_update \
-H "Authorization: Bearer $ERA_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{"ids":{}}'{
"ok": true,
"result": "…",
"events": [
"inbox.assigned",
"inbox.team_assigned",
"inbox.resolved",
"inbox.reopened",
"inbox.snoozed",
"inbox.priority_changed",
"inbox.message_sent"
]
}import { createEra } from "@getera/sdk"
const era = createEra({ apiKey: process.env.ERA_API_KEY })
const res = await era.messages.bulkUpdate({
ids: {}
})era messages bulk_update --data '{"ids":{}}'{
"name": "era__inbox__bulk_update",
"arguments": {
"ids": {}
}
}Errors: 401 no or expired credential · 403 the credential does not hold inbox:write · 404 the record or module is outside what it may reach · 422 invalid input (problems[]) · 409 invalid_selection, nothing_to_change · 429 slow down
inbox.change_customer
Move a conversation to another person — Corrects who a conversation is with, keeping the channel address it arrived on.
Needs inbox:write · changes data
https://app.getera.io/api/v1/conversations/{id}/actions/change_customer- Canonical address:
POST /api/v1/actions/inbox.change_customer
Input
| Field | Type | Required | Notes |
|---|---|---|---|
id | uuid | yes | Filled from the path {id} · Conversation · id of conversation |
customerId | uuid | yes | Person · id of customer |
Preconditions
The action is refused with 409 and one of these codes:
| Code | Refused when |
|---|---|
conversation_not_found | The conversation must exist. |
customer_not_found | The person must already be on file. |
customer_archived | An archived person cannot be written to. |
Events it publishes: inbox.customer_changed
What it writes: conversation (update) — Repoints the conversation
curl -X POST https://app.getera.io/api/v1/conversations/CONVERSATION_ID/actions/change_customer \
-H "Authorization: Bearer $ERA_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{"customerId":"CUSTOMER_ID"}'{
"ok": true,
"result": "…",
"events": [
"inbox.customer_changed"
]
}import { createEra } from "@getera/sdk"
const era = createEra({ apiKey: process.env.ERA_API_KEY })
const res = await era.conversations.changeCustomer("CONVERSATION_ID", {
customerId: "CUSTOMER_ID"
})era conversations change_customer CONVERSATION_ID \
--customer-id "CUSTOMER_ID"{
"name": "era__inbox__change_customer",
"arguments": {
"id": "CONVERSATION_ID",
"customerId": "CUSTOMER_ID"
}
}Errors: 401 no or expired credential · 403 the credential does not hold inbox:write · 404 the record or module is outside what it may reach · 422 invalid input (problems[]) · 409 conversation_not_found, customer_not_found, customer_archived · 429 slow down
inbox.create_inbox
Connect a channel — Opens a new inbox on one of the supported channels.
Needs inbox:manage · changes data
https://app.getera.io/api/v1/inboxes/actions/create_inbox- REST alias:
POST /api/v1/inboxes - Canonical address:
POST /api/v1/actions/inbox.create_inbox
Input
| Field | Type | Required | Notes |
|---|---|---|---|
name | string | yes | Name · max 200 |
channelType | string (enum) | yes | Channel · Allowed values: web_widget, api, email, whatsapp, sms, twilio_sms, facebook_page, instagram, telegram, line, tiktok, twitter, voice |
identifier | string | Number or account · The phone number, page id, bot name or token this channel answers on. · max 200 | |
channelConfig | object / array | Channel settings | |
credentialId | uuid | Credential | |
memberUserIds | object / array | Who works it |
Preconditions
The action is refused with 409 and one of these codes:
| Code | Refused when |
|---|---|
identifier_taken | A number or account may only serve one inbox. |
channel_config_incomplete | A channel must be given everything its provider needs before it is connected. |
not_a_member | Everyone assigned to it must be an active member. |
mailbox_connection_required | A mailbox signed in with Gmail or Outlook names the account. |
mailbox_not_mail_app | That account is a Gmail or Outlook connection. |
connection_not_usable | That account is this person’s, the company’s, or shared with them. |
connection_not_found | The account is still connected in this workspace. |
connection_inactive | The account is active, not paused or revoked. |
Events it publishes: inbox.channel_connected
What it writes: inbox (insert) — Opens the channel; inbox_member (insert) — Records who works it
curl -X POST https://app.getera.io/api/v1/inboxes/actions/create_inbox \
-H "Authorization: Bearer $ERA_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{"name":"Name","channelType":"web_widget"}'{
"ok": true,
"result": "…",
"events": [
"inbox.channel_connected"
]
}import { createEra } from "@getera/sdk"
const era = createEra({ apiKey: process.env.ERA_API_KEY })
const res = await era.inboxes.create({
name: "Name",
channelType: "web_widget"
})era inboxes create \
--name "Name" \
--channel-type "web_widget"{
"name": "era__inbox__create_inbox",
"arguments": {
"name": "Name",
"channelType": "web_widget"
}
}Errors: 401 no or expired credential · 403 the credential does not hold inbox:manage · 404 the record or module is outside what it may reach · 422 invalid input (problems[]) · 409 identifier_taken, channel_config_incomplete, not_a_member, mailbox_connection_required, mailbox_not_mail_app, connection_not_usable, connection_not_found, connection_inactive · 429 slow down
inbox.deactivate_inbox
Take a channel out of service — Stops an inbox taking new conversations, keeping everything it already holds.
Needs inbox:manage · reaches outside (sends, publishes or charges) · asks a person to confirm when Era runs it
https://app.getera.io/api/v1/inboxes/{id}/actions/deactivate_inbox- Canonical address:
POST /api/v1/actions/inbox.deactivate_inbox
Input
| Field | Type | Required | Notes |
|---|---|---|---|
id | uuid | yes | Filled from the path {id} · Inbox · id of inbox |
Preconditions
The action is refused with 409 and one of these codes:
| Code | Refused when |
|---|---|
inbox_not_found | The inbox must exist. |
inbox_has_open_conversations | Open conversations are resolved or moved before a channel is closed. |
Events it publishes: inbox.channel_deactivated
What it writes: inbox (update) — Marks the channel inactive
curl -X POST https://app.getera.io/api/v1/inboxes/INBOXE_ID/actions/deactivate_inbox \
-H "Authorization: Bearer $ERA_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{}'{
"ok": true,
"result": "…",
"events": [
"inbox.channel_deactivated"
]
}import { createEra } from "@getera/sdk"
const era = createEra({ apiKey: process.env.ERA_API_KEY })
const res = await era.inboxes.deactivateInbox("INBOXE_ID")era inboxes deactivate_inbox INBOXE_ID{
"name": "era__inbox__deactivate_inbox",
"arguments": {
"id": "INBOXE_ID"
}
}Errors: 401 no or expired credential · 403 the credential does not hold inbox:manage · 404 the record or module is outside what it may reach · 422 invalid input (problems[]) · 409 inbox_not_found, inbox_has_open_conversations · 429 slow down
inbox.delete_agent_bot
Remove a bot — Removes the bot; conversations it was holding go to the team.
Needs inbox:manage · removes or cannot be undone · asks a person to confirm when Era runs it
https://app.getera.io/api/v1/agent_bots/{id}/actions/delete_agent_bot- REST alias:
DELETE /api/v1/agent_bots/{id} - Canonical address:
POST /api/v1/actions/inbox.delete_agent_bot
Input
| Field | Type | Required | Notes |
|---|---|---|---|
id | uuid | yes | Filled from the path {id} · Bot · id of agent_bot |
Preconditions
The action is refused with 409 and one of these codes:
| Code | Refused when |
|---|---|
agent_bot_not_found | The bot must exist. |
Events it publishes: inbox.agent_bot_changed
What it writes: conversation (update) — Hands its pending conversations to people; agent_bot (delete) — Removes the bot and its inbox bindings
curl -X POST https://app.getera.io/api/v1/agent_bots/AGENT_BOT_ID/actions/delete_agent_bot \
-H "Authorization: Bearer $ERA_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{}'{
"ok": true,
"result": "…",
"events": [
"inbox.agent_bot_changed"
]
}import { createEra } from "@getera/sdk"
const era = createEra({ apiKey: process.env.ERA_API_KEY })
const res = await era.agentBots.deleteAgentBot("AGENT_BOT_ID")era agent_bots delete_agent_bot AGENT_BOT_ID{
"name": "era__inbox__delete_agent_bot",
"arguments": {
"id": "AGENT_BOT_ID"
}
}Errors: 401 no or expired credential · 403 the credential does not hold inbox:manage · 404 the record or module is outside what it may reach · 422 invalid input (problems[]) · 409 agent_bot_not_found · 429 slow down
inbox.delete_canned_response
Delete a saved reply — Removes a shortcut from the composer.
Needs inbox:manage · removes or cannot be undone · asks a person to confirm when Era runs it
https://app.getera.io/api/v1/canned_responses/{id}/actions/delete_canned_response- REST alias:
DELETE /api/v1/canned_responses/{id} - Canonical address:
POST /api/v1/actions/inbox.delete_canned_response
Input
| Field | Type | Required | Notes |
|---|---|---|---|
id | uuid | yes | Filled from the path {id} · Saved reply · id of canned_response |
Preconditions
The action is refused with 409 and one of these codes:
| Code | Refused when |
|---|---|
canned_response_not_found | The saved reply must exist. |
Events it publishes: inbox.canned_response_changed
What it writes: canned_response (delete) — Removes the saved reply
curl -X POST https://app.getera.io/api/v1/canned_responses/CANNED_RESPONSE_ID/actions/delete_canned_response \
-H "Authorization: Bearer $ERA_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{}'{
"ok": true,
"result": "…",
"events": [
"inbox.canned_response_changed"
]
}import { createEra } from "@getera/sdk"
const era = createEra({ apiKey: process.env.ERA_API_KEY })
const res = await era.cannedResponses.deleteCannedResponse("CANNED_RESPONSE_ID")era canned_responses delete_canned_response CANNED_RESPONSE_ID{
"name": "era__inbox__delete_canned_response",
"arguments": {
"id": "CANNED_RESPONSE_ID"
}
}Errors: 401 no or expired credential · 403 the credential does not hold inbox:manage · 404 the record or module is outside what it may reach · 422 invalid input (problems[]) · 409 canned_response_not_found · 429 slow down
inbox.delete_macro
Delete a macro — Removes a macro.
Needs inbox:write · removes or cannot be undone · asks a person to confirm when Era runs it
https://app.getera.io/api/v1/macros/{id}/actions/delete_macro- REST alias:
DELETE /api/v1/macros/{id} - Canonical address:
POST /api/v1/actions/inbox.delete_macro
Input
| Field | Type | Required | Notes |
|---|---|---|---|
id | uuid | yes | Filled from the path {id} · Macro · id of macro |
Preconditions
The action is refused with 409 and one of these codes:
| Code | Refused when |
|---|---|
macro_not_found | The macro must exist and be visible to you. |
not_macro_owner | Only its author or a manager deletes a macro. |
Events it publishes: inbox.macro_changed
What it writes: macro (delete) — Removes the macro
curl -X POST https://app.getera.io/api/v1/macros/MACRO_ID/actions/delete_macro \
-H "Authorization: Bearer $ERA_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{}'{
"ok": true,
"result": "…",
"events": [
"inbox.macro_changed"
]
}import { createEra } from "@getera/sdk"
const era = createEra({ apiKey: process.env.ERA_API_KEY })
const res = await era.macros.deleteMacro("MACRO_ID")era macros delete_macro MACRO_ID{
"name": "era__inbox__delete_macro",
"arguments": {
"id": "MACRO_ID"
}
}Errors: 401 no or expired credential · 403 the credential does not hold inbox:write · 404 the record or module is outside what it may reach · 422 invalid input (problems[]) · 409 macro_not_found, not_macro_owner · 429 slow down
inbox.delete_message
Delete a message — Removes a message from the thread, keeping the row for the audit trail.
Needs inbox:delete · removes or cannot be undone · asks a person to confirm when Era runs it
https://app.getera.io/api/v1/messages/{id}/actions/delete_message- REST alias:
DELETE /api/v1/messages/{id} - Canonical address:
POST /api/v1/actions/inbox.delete_message
Input
| Field | Type | Required | Notes |
|---|---|---|---|
id | uuid | yes | Filled from the path {id} · Message · id of message |
Preconditions
The action is refused with 409 and one of these codes:
| Code | Refused when |
|---|---|
message_not_found | The message must exist. |
Events it publishes: inbox.message_deleted
What it writes: message (update) — Writes the tombstone
curl -X POST https://app.getera.io/api/v1/messages/MESSAGE_ID/actions/delete_message \
-H "Authorization: Bearer $ERA_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{}'{
"ok": true,
"result": "…",
"events": [
"inbox.message_deleted"
]
}import { createEra } from "@getera/sdk"
const era = createEra({ apiKey: process.env.ERA_API_KEY })
const res = await era.messages.deleteMessage("MESSAGE_ID")era messages delete_message MESSAGE_ID{
"name": "era__inbox__delete_message",
"arguments": {
"id": "MESSAGE_ID"
}
}Errors: 401 no or expired credential · 403 the credential does not hold inbox:delete · 404 the record or module is outside what it may reach · 422 invalid input (problems[]) · 409 message_not_found · 429 slow down
inbox.delete_sla_policy
Delete an SLA policy — Removes a policy nothing has been measured against.
Needs inbox:manage · removes or cannot be undone · asks a person to confirm when Era runs it
https://app.getera.io/api/v1/sla_policies/{id}/actions/delete_sla_policy- REST alias:
DELETE /api/v1/sla_policies/{id} - Canonical address:
POST /api/v1/actions/inbox.delete_sla_policy
Input
| Field | Type | Required | Notes |
|---|---|---|---|
id | uuid | yes | Filled from the path {id} · Policy · id of sla_policy |
Preconditions
The action is refused with 409 and one of these codes:
| Code | Refused when |
|---|---|
sla_policy_not_found | The policy must exist. |
sla_policy_in_use | A policy with applied conversations keeps its history. |
Events it publishes: inbox.sla_policy_changed
What it writes: sla_policy (delete) — Deletes the policy
curl -X POST https://app.getera.io/api/v1/sla_policies/SLA_POLICY_ID/actions/delete_sla_policy \
-H "Authorization: Bearer $ERA_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{}'{
"ok": true,
"result": "…",
"events": [
"inbox.sla_policy_changed"
]
}import { createEra } from "@getera/sdk"
const era = createEra({ apiKey: process.env.ERA_API_KEY })
const res = await era.slaPolicies.deleteSlaPolicy("SLA_POLICY_ID")era sla_policies delete_sla_policy SLA_POLICY_ID{
"name": "era__inbox__delete_sla_policy",
"arguments": {
"id": "SLA_POLICY_ID"
}
}Errors: 401 no or expired credential · 403 the credential does not hold inbox:manage · 404 the record or module is outside what it may reach · 422 invalid input (problems[]) · 409 sla_policy_not_found, sla_policy_in_use · 429 slow down
inbox.delete_team
Delete a team — Removes a team, leaving its conversations without one.
Needs inbox:manage · removes or cannot be undone · asks a person to confirm when Era runs it
https://app.getera.io/api/v1/teams/{id}/actions/delete_team- REST alias:
DELETE /api/v1/teams/{id} - Canonical address:
POST /api/v1/actions/inbox.delete_team
Input
| Field | Type | Required | Notes |
|---|---|---|---|
id | uuid | yes | Filled from the path {id} · Team · id of team |
force | boolean | Delete anyway |
Preconditions
The action is refused with 409 and one of these codes:
| Code | Refused when |
|---|---|
team_not_found | The team must exist. |
team_has_open_conversations | Open conversations on the team are counted before it is removed. |
Events it publishes: inbox.team_changed
What it writes: team (delete) — Removes the team
curl -X POST https://app.getera.io/api/v1/teams/TEAM_ID/actions/delete_team \
-H "Authorization: Bearer $ERA_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{"force":true}'{
"ok": true,
"result": "…",
"events": [
"inbox.team_changed"
]
}import { createEra } from "@getera/sdk"
const era = createEra({ apiKey: process.env.ERA_API_KEY })
const res = await era.teams.deleteTeam("TEAM_ID", {
force: true
})era teams delete_team TEAM_ID \
--force true{
"name": "era__inbox__delete_team",
"arguments": {
"id": "TEAM_ID",
"force": true
}
}Errors: 401 no or expired credential · 403 the credential does not hold inbox:manage · 404 the record or module is outside what it may reach · 422 invalid input (problems[]) · 409 team_not_found, team_has_open_conversations · 429 slow down
inbox.edit_message
Edit a message — Corrects a message you sent.
Needs inbox:write · reaches outside (sends, publishes or charges)
https://app.getera.io/api/v1/messages/{id}/actions/edit_message- Canonical address:
POST /api/v1/actions/inbox.edit_message
Input
| Field | Type | Required | Notes |
|---|---|---|---|
id | uuid | yes | Filled from the path {id} · Message · id of message |
content | string | yes | Message · max 150000 |
Preconditions
The action is refused with 409 and one of these codes:
| Code | Refused when |
|---|---|
message_not_found | The message must exist. |
message_not_editable | Only an outgoing message can be edited. |
empty_message | A message needs words or an attachment. |
Events it publishes: inbox.message_edited
What it writes: message (update) — Rewrites the message and marks it edited
curl -X POST https://app.getera.io/api/v1/messages/MESSAGE_ID/actions/edit_message \
-H "Authorization: Bearer $ERA_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{"content":"Message"}'{
"ok": true,
"result": "…",
"events": [
"inbox.message_edited"
]
}import { createEra } from "@getera/sdk"
const era = createEra({ apiKey: process.env.ERA_API_KEY })
const res = await era.messages.editMessage("MESSAGE_ID", {
content: "Message"
})era messages edit_message MESSAGE_ID \
--content "Message"{
"name": "era__inbox__edit_message",
"arguments": {
"id": "MESSAGE_ID",
"content": "Message"
}
}Errors: 401 no or expired credential · 403 the credential does not hold inbox:write · 404 the record or module is outside what it may reach · 422 invalid input (problems[]) · 409 message_not_found, message_not_editable, empty_message · 429 slow down
inbox.evaluate_sla
Check an SLA — Records the targets a conversation has missed, and settles hit or missed once it is resolved.
Needs inbox:handle · changes data
https://app.getera.io/api/v1/applied_slas/{id}/actions/evaluate_sla- Canonical address:
POST /api/v1/actions/inbox.evaluate_sla
Input
| Field | Type | Required | Notes |
|---|---|---|---|
id | uuid | yes | Filled from the path {id} · Applied SLA · id of applied_sla |
Preconditions
The action is refused with 409 and one of these codes:
| Code | Refused when |
|---|---|
applied_sla_not_found | The applied SLA must exist. |
Events it publishes: inbox.sla_missed, inbox.sla_closed
What it writes: applied_sla (update) — Writes the status and the next deadline; sla_event (insert) — One row per missed target
curl -X POST https://app.getera.io/api/v1/applied_slas/APPLIED_SLA_ID/actions/evaluate_sla \
-H "Authorization: Bearer $ERA_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{}'{
"ok": true,
"result": "…",
"events": [
"inbox.sla_missed",
"inbox.sla_closed"
]
}import { createEra } from "@getera/sdk"
const era = createEra({ apiKey: process.env.ERA_API_KEY })
const res = await era.appliedSlas.evaluateSla("APPLIED_SLA_ID")era applied_slas evaluate_sla APPLIED_SLA_ID{
"name": "era__inbox__evaluate_sla",
"arguments": {
"id": "APPLIED_SLA_ID"
}
}Errors: 401 no or expired credential · 403 the credential does not hold inbox:handle · 404 the record or module is outside what it may reach · 422 invalid input (problems[]) · 409 applied_sla_not_found · 429 slow down
inbox.ingest
Take in an arriving message — Files a message that came in on a channel, opening the conversation if it is new.
Needs inbox:handle · changes data
https://app.getera.io/api/v1/conversations/actions/ingest- Canonical address:
POST /api/v1/actions/inbox.ingest
Input
| Field | Type | Required | Notes |
|---|---|---|---|
inboxId | uuid | yes | Inbox · id of inbox |
customerId | uuid | yes | Person · id of customer |
sourceId | string | yes | The provider’s id for this message · What makes a redelivered webhook one message rather than two. · max 200 |
content | string | What they said · max 150000 | |
senderName | string | Name at the provider · max 200 | |
customerIdentityId | uuid | Channel address · id of customer_identity | |
channelAddress | string | Channel address value · The customer_identity value of kind channel — the thread is keyed on it. · max 300 | |
contentType | string (enum) | Kind of message · Allowed values: text, input_text, input_textarea, input_email, input_select, cards, form, article, incoming_email, input_csat, integrations, sticker, voice_call | |
attachments | object / array | Attachments | |
inReplyTo | object / array | Replies to (email ids) | |
newThread | boolean | Start a new thread unless replying |
Preconditions
The action is refused with 409 and one of these codes:
| Code | Refused when |
|---|---|
inbox_not_found | The inbox must exist. |
inbox_inactive | The channel must still be in service. |
no_source_id | An arriving message must carry the provider’s id for it. |
empty_message | A message needs words or an attachment. |
malformed_message | Attachments and replies-to are lists. |
duplicate_message | The same delivery is only ever filed once. |
customer_not_found | The person must already be on file. |
customer_archived | An archived person cannot be written to. |
Events it publishes: inbox.message_received, inbox.conversation_opened, inbox.reopened, inbox.message_sent
What it writes: conversation (upsert) — Finds the thread, or opens a new one; message (insert) — Files what arrived; message_attachment (insert) — Files what came with it; reporting_event (insert) — Writes the report clock reading (sql/126)
curl -X POST https://app.getera.io/api/v1/conversations/actions/ingest \
-H "Authorization: Bearer $ERA_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{"inboxId":"INBOX_ID","customerId":"CUSTOMER_ID","sourceId":"The provider’s id for this message"}'{
"ok": true,
"result": "…",
"events": [
"inbox.message_received",
"inbox.conversation_opened",
"inbox.reopened",
"inbox.message_sent"
]
}import { createEra } from "@getera/sdk"
const era = createEra({ apiKey: process.env.ERA_API_KEY })
const res = await era.conversations.ingest({
inboxId: "INBOX_ID",
customerId: "CUSTOMER_ID",
sourceId: "The provider’s id for this message"
})era conversations ingest \
--inbox-id "INBOX_ID" \
--customer-id "CUSTOMER_ID" \
--source-id "The provider’s id for this message"{
"name": "era__inbox__ingest",
"arguments": {
"inboxId": "INBOX_ID",
"customerId": "CUSTOMER_ID",
"sourceId": "The provider’s id for this message"
}
}Errors: 401 no or expired credential · 403 the credential does not hold inbox:handle · 404 the record or module is outside what it may reach · 422 invalid input (problems[]) · 409 inbox_not_found, inbox_inactive, no_source_id, empty_message, malformed_message, duplicate_message, customer_not_found, customer_archived · 429 slow down
inbox.mark_read
Mark read — Records that the thread has been seen.
Needs inbox:read · changes data
https://app.getera.io/api/v1/conversations/{id}/actions/mark_read- Canonical address:
POST /api/v1/actions/inbox.mark_read
Input
| Field | Type | Required | Notes |
|---|---|---|---|
id | uuid | yes | Filled from the path {id} · Conversation · id of conversation |
Preconditions
The action is refused with 409 and one of these codes:
| Code | Refused when |
|---|---|
conversation_not_found | The conversation must exist. |
What it writes: conversation (update) — Stamps the read marks
curl -X POST https://app.getera.io/api/v1/conversations/CONVERSATION_ID/actions/mark_read \
-H "Authorization: Bearer $ERA_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{}'{
"ok": true,
"result": "…",
"events": []
}import { createEra } from "@getera/sdk"
const era = createEra({ apiKey: process.env.ERA_API_KEY })
const res = await era.conversations.markRead("CONVERSATION_ID")era conversations mark_read CONVERSATION_ID{
"name": "era__inbox__mark_read",
"arguments": {
"id": "CONVERSATION_ID"
}
}Errors: 401 no or expired credential · 403 the credential does not hold inbox:read · 404 the record or module is outside what it may reach · 422 invalid input (problems[]) · 409 conversation_not_found · 429 slow down
inbox.mark_unread
Mark unread — Moves the read marker behind the first incoming message so the conversation returns to the unread queue.
Needs inbox:read · changes data
https://app.getera.io/api/v1/conversations/{id}/actions/mark_unread- Canonical address:
POST /api/v1/actions/inbox.mark_unread
Input
| Field | Type | Required | Notes |
|---|---|---|---|
id | uuid | yes | Filled from the path {id} · Conversation · id of conversation |
Preconditions
The action is refused with 409 and one of these codes:
| Code | Refused when |
|---|---|
conversation_not_found | The conversation must exist. |
What it writes: conversation (update) — Moves the read marker back
curl -X POST https://app.getera.io/api/v1/conversations/CONVERSATION_ID/actions/mark_unread \
-H "Authorization: Bearer $ERA_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{}'{
"ok": true,
"result": "…",
"events": []
}import { createEra } from "@getera/sdk"
const era = createEra({ apiKey: process.env.ERA_API_KEY })
const res = await era.conversations.markUnread("CONVERSATION_ID")era conversations mark_unread CONVERSATION_ID{
"name": "era__inbox__mark_unread",
"arguments": {
"id": "CONVERSATION_ID"
}
}Errors: 401 no or expired credential · 403 the credential does not hold inbox:read · 404 the record or module is outside what it may reach · 422 invalid input (problems[]) · 409 conversation_not_found · 429 slow down
inbox.mute
Mute — Resolves the conversation and files what the person sends next without reopening it.
Needs inbox:write · changes data
https://app.getera.io/api/v1/conversations/{id}/actions/mute- Canonical address:
POST /api/v1/actions/inbox.mute
Input
| Field | Type | Required | Notes |
|---|---|---|---|
id | uuid | yes | Filled from the path {id} · Conversation · id of conversation |
Preconditions
The action is refused with 409 and one of these codes:
| Code | Refused when |
|---|---|
conversation_not_found | The conversation must exist. |
already_muted | It must not already be muted. |
Events it publishes: inbox.muted
What it writes: conversation (update) — Mutes and resolves it; reporting_event (insert) — Writes the report clock reading (sql/126)
curl -X POST https://app.getera.io/api/v1/conversations/CONVERSATION_ID/actions/mute \
-H "Authorization: Bearer $ERA_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{}'{
"ok": true,
"result": "…",
"events": [
"inbox.muted"
]
}import { createEra } from "@getera/sdk"
const era = createEra({ apiKey: process.env.ERA_API_KEY })
const res = await era.conversations.mute("CONVERSATION_ID")era conversations mute CONVERSATION_ID{
"name": "era__inbox__mute",
"arguments": {
"id": "CONVERSATION_ID"
}
}Errors: 401 no or expired credential · 403 the credential does not hold inbox:write · 404 the record or module is outside what it may reach · 422 invalid input (problems[]) · 409 conversation_not_found, already_muted · 429 slow down
inbox.open_conversation
Open a conversation — Starts a conversation with somebody on one of your channels.
Needs inbox:write · changes data
https://app.getera.io/api/v1/conversations/actions/open_conversation- REST alias:
POST /api/v1/conversations - Canonical address:
POST /api/v1/actions/inbox.open_conversation
Input
| Field | Type | Required | Notes |
|---|---|---|---|
inboxId | uuid | yes | Inbox · id of inbox |
customerId | uuid | yes | Person · id of customer |
customerIdentityId | uuid | Channel address · id of customer_identity | |
teamId | uuid | Team · id of team | |
priority | string (enum) | Priority · Allowed values: low, medium, high, urgent | |
externalIdentifier | string | Channel reference · max 200 |
Preconditions
The action is refused with 409 and one of these codes:
| Code | Refused when |
|---|---|
inbox_not_found | The inbox must exist. |
inbox_inactive | The channel must still be in service. |
customer_not_found | The person must already be on file. |
customer_archived | An archived person cannot be written to. |
identity_not_theirs | The channel address must belong to that person. |
conversation_already_open | An inbox locked to one conversation per person refuses a second. |
team_not_found | The team must exist. |
Events it publishes: inbox.conversation_opened
What it writes: conversation (insert) — Opens the conversation
curl -X POST https://app.getera.io/api/v1/conversations/actions/open_conversation \
-H "Authorization: Bearer $ERA_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{"inboxId":"INBOX_ID","customerId":"CUSTOMER_ID"}'{
"ok": true,
"result": "…",
"events": [
"inbox.conversation_opened"
]
}import { createEra } from "@getera/sdk"
const era = createEra({ apiKey: process.env.ERA_API_KEY })
const res = await era.conversations.create({
inboxId: "INBOX_ID",
customerId: "CUSTOMER_ID"
})era conversations create \
--inbox-id "INBOX_ID" \
--customer-id "CUSTOMER_ID"{
"name": "era__inbox__open_conversation",
"arguments": {
"inboxId": "INBOX_ID",
"customerId": "CUSTOMER_ID"
}
}Errors: 401 no or expired credential · 403 the credential does not hold inbox:write · 404 the record or module is outside what it may reach · 422 invalid input (problems[]) · 409 inbox_not_found, inbox_inactive, customer_not_found, customer_archived, identity_not_theirs, conversation_already_open, team_not_found · 429 slow down
inbox.record_bot_delivery
Record a bot delivery — Machine-only: what the last event sent to a bot did.
Needs inbox:handle · changes data
403 machine_only).https://app.getera.io/api/v1/agent_bots/{id}/actions/record_bot_delivery- Canonical address:
POST /api/v1/actions/inbox.record_bot_delivery
Input
| Field | Type | Required | Notes |
|---|---|---|---|
agentBotId | uuid | yes | Filled from the path {id} · Bot · id of agent_bot |
error | string | What went wrong · max 2000 |
Preconditions
The action is refused with 409 and one of these codes:
| Code | Refused when |
|---|---|
agent_bot_not_found | The bot must exist. |
What it writes: agent_bot (update) — Stamps the last delivery and its error
Errors: 401 no or expired credential · 403 the credential does not hold inbox:handle · 404 the record or module is outside what it may reach · 422 invalid input (problems[]) · 409 agent_bot_not_found · 429 slow down
inbox.record_channel_health
Record a connection test — Writes whether a channel can currently reach its provider.
Needs inbox:handle · changes data
https://app.getera.io/api/v1/inboxes/{id}/actions/record_channel_health- Canonical address:
POST /api/v1/actions/inbox.record_channel_health
Input
| Field | Type | Required | Notes |
|---|---|---|---|
inboxId | uuid | yes | Filled from the path {id} · Inbox · id of inbox |
status | string (enum) | yes | Status · Allowed values: healthy, degraded, reauth_required |
error | string | Reason · max 2000 | |
details | object / array | Provider details |
Preconditions
The action is refused with 409 and one of these codes:
| Code | Refused when |
|---|---|
inbox_not_found | The inbox must exist. |
Events it publishes: inbox.channel_health_changed
What it writes: inbox (update) — Writes the health status, reason and provider details (quality rating, tier)
curl -X POST https://app.getera.io/api/v1/inboxes/INBOXE_ID/actions/record_channel_health \
-H "Authorization: Bearer $ERA_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{"status":"healthy"}'{
"ok": true,
"result": "…",
"events": [
"inbox.channel_health_changed"
]
}import { createEra } from "@getera/sdk"
const era = createEra({ apiKey: process.env.ERA_API_KEY })
const res = await era.inboxes.recordChannelHealth("INBOXE_ID", {
status: "healthy"
})era inboxes record_channel_health INBOXE_ID \
--status "healthy"{
"name": "era__inbox__record_channel_health",
"arguments": {
"inboxId": "INBOXE_ID",
"status": "healthy"
}
}Errors: 401 no or expired credential · 403 the credential does not hold inbox:handle · 404 the record or module is outside what it may reach · 422 invalid input (problems[]) · 409 inbox_not_found · 429 slow down
inbox.record_delivery
Record what a channel said — Moves an outgoing message to sent, delivered, read or failed, as the channel reports it.
Needs inbox:handle · changes data
https://app.getera.io/api/v1/messages/{id}/actions/record_delivery- Canonical address:
POST /api/v1/actions/inbox.record_delivery
Input
| Field | Type | Required | Notes |
|---|---|---|---|
messageId | uuid | Filled from the path {id} · Message · id of message | |
inboxId | uuid | Inbox · id of inbox | |
sourceId | string | Provider message id · max 300 | |
status | string (enum) | yes | Status · Allowed values: queued, sent, delivered, read, failed |
generation | number | Attempt generation · min 0 | |
providerMessageId | string | Provider message id · max 300 | |
error | string | Reason · max 2000 | |
attempts | number | Attempts · min 0 · max 100 |
Preconditions
The action is refused with 409 and one of these codes:
| Code | Refused when |
|---|---|
message_not_found | The message must exist. |
message_not_outgoing | Only a message that goes out has a delivery state. |
Events it publishes: inbox.message_delivered, inbox.message_failed
What it writes: message (update) — Writes the delivery state and the provider id
curl -X POST https://app.getera.io/api/v1/messages/MESSAGE_ID/actions/record_delivery \
-H "Authorization: Bearer $ERA_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{"status":"queued"}'{
"ok": true,
"result": "…",
"events": [
"inbox.message_delivered",
"inbox.message_failed"
]
}import { createEra } from "@getera/sdk"
const era = createEra({ apiKey: process.env.ERA_API_KEY })
const res = await era.messages.recordDelivery("MESSAGE_ID", {
status: "queued"
})era messages record_delivery MESSAGE_ID \
--status "queued"{
"name": "era__inbox__record_delivery",
"arguments": {
"messageId": "MESSAGE_ID",
"status": "queued"
}
}Errors: 401 no or expired credential · 403 the credential does not hold inbox:handle · 404 the record or module is outside what it may reach · 422 invalid input (problems[]) · 409 message_not_found, message_not_outgoing · 429 slow down
inbox.record_poll_state
Record the mailbox cursor — Remembers which messages the email poller has already filed.
Needs inbox:handle · changes data
https://app.getera.io/api/v1/inboxes/{id}/actions/record_poll_state- Canonical address:
POST /api/v1/actions/inbox.record_poll_state
Input
| Field | Type | Required | Notes |
|---|---|---|---|
inboxId | uuid | yes | Filled from the path {id} · Inbox · id of inbox |
pollState | object / array | yes | Cursor |
Preconditions
The action is refused with 409 and one of these codes:
| Code | Refused when |
|---|---|
inbox_not_found | The inbox must exist. |
invalid_poll_state | The cursor is an object. |
What it writes: inbox (update) — Writes the poll cursor
curl -X POST https://app.getera.io/api/v1/inboxes/INBOXE_ID/actions/record_poll_state \
-H "Authorization: Bearer $ERA_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{"pollState":{}}'{
"ok": true,
"result": "…",
"events": []
}import { createEra } from "@getera/sdk"
const era = createEra({ apiKey: process.env.ERA_API_KEY })
const res = await era.inboxes.recordPollState("INBOXE_ID", {
pollState: {}
})era inboxes record_poll_state INBOXE_ID --data '{"pollState":{}}'{
"name": "era__inbox__record_poll_state",
"arguments": {
"inboxId": "INBOXE_ID",
"pollState": {}
}
}Errors: 401 no or expired credential · 403 the credential does not hold inbox:handle · 404 the record or module is outside what it may reach · 422 invalid input (problems[]) · 409 inbox_not_found, invalid_poll_state · 429 slow down
inbox.record_visitor_seen
Record that the visitor saw the thread — Stamps what the website visitor has read, and marks those replies read.
Needs inbox:handle · changes data
https://app.getera.io/api/v1/conversations/{id}/actions/record_visitor_seen- Canonical address:
POST /api/v1/actions/inbox.record_visitor_seen
Input
| Field | Type | Required | Notes |
|---|---|---|---|
conversationId | uuid | yes | Filled from the path {id} · Conversation · id of conversation |
Preconditions
The action is refused with 409 and one of these codes:
| Code | Refused when |
|---|---|
conversation_not_found | The conversation must exist. |
What it writes: conversation (update) — Stamps contact_last_seen_at; message (update) — Marks the replies it covered read
curl -X POST https://app.getera.io/api/v1/conversations/CONVERSATION_ID/actions/record_visitor_seen \
-H "Authorization: Bearer $ERA_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{}'{
"ok": true,
"result": "…",
"events": []
}import { createEra } from "@getera/sdk"
const era = createEra({ apiKey: process.env.ERA_API_KEY })
const res = await era.conversations.recordVisitorSeen("CONVERSATION_ID")era conversations record_visitor_seen CONVERSATION_ID{
"name": "era__inbox__record_visitor_seen",
"arguments": {
"conversationId": "CONVERSATION_ID"
}
}Errors: 401 no or expired credential · 403 the credential does not hold inbox:handle · 404 the record or module is outside what it may reach · 422 invalid input (problems[]) · 409 conversation_not_found · 429 slow down
inbox.remove_participant
Remove a follower — Stops somebody following a conversation.
Needs inbox:write · changes data
https://app.getera.io/api/v1/conversation_participants/actions/remove_participant- Canonical address:
POST /api/v1/actions/inbox.remove_participant
Input
| Field | Type | Required | Notes |
|---|---|---|---|
id | uuid | yes | Conversation · id of conversation |
userId | uuid | yes | Person |
Preconditions
The action is refused with 409 and one of these codes:
| Code | Refused when |
|---|---|
conversation_not_found | The conversation must exist. |
not_participating | They must currently be following it. |
What it writes: conversation_participant (delete) — Removes the follower
curl -X POST https://app.getera.io/api/v1/conversation_participants/actions/remove_participant \
-H "Authorization: Bearer $ERA_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{"id":"CONVERSATION_ID","userId":"USER_ID"}'{
"ok": true,
"result": "…",
"events": []
}import { createEra } from "@getera/sdk"
const era = createEra({ apiKey: process.env.ERA_API_KEY })
const res = await era.conversationParticipants.removeParticipant({
id: "CONVERSATION_ID",
userId: "USER_ID"
})era conversation_participants remove_participant \
--id "CONVERSATION_ID" \
--user-id "USER_ID"{
"name": "era__inbox__remove_participant",
"arguments": {
"id": "CONVERSATION_ID",
"userId": "USER_ID"
}
}Errors: 401 no or expired credential · 403 the credential does not hold inbox:write · 404 the record or module is outside what it may reach · 422 invalid input (problems[]) · 409 conversation_not_found, not_participating · 429 slow down
inbox.reopen
Reopen a conversation — Brings a resolved or snoozed conversation back into the queue.
Needs inbox:write · changes data
https://app.getera.io/api/v1/conversations/{id}/actions/reopen- Canonical address:
POST /api/v1/actions/inbox.reopen
Input
| Field | Type | Required | Notes |
|---|---|---|---|
id | uuid | yes | Filled from the path {id} · Conversation · id of conversation |
Preconditions
The action is refused with 409 and one of these codes:
| Code | Refused when |
|---|---|
conversation_not_found | The conversation must exist. |
already_open | It must not already be open. |
Events it publishes: inbox.reopened
What it writes: conversation (update) — Reopens it; reporting_event (insert) — Writes the report clock reading (sql/126)
curl -X POST https://app.getera.io/api/v1/conversations/CONVERSATION_ID/actions/reopen \
-H "Authorization: Bearer $ERA_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{}'{
"ok": true,
"result": "…",
"events": [
"inbox.reopened"
]
}import { createEra } from "@getera/sdk"
const era = createEra({ apiKey: process.env.ERA_API_KEY })
const res = await era.conversations.reopen("CONVERSATION_ID")era conversations reopen CONVERSATION_ID{
"name": "era__inbox__reopen",
"arguments": {
"id": "CONVERSATION_ID"
}
}Errors: 401 no or expired credential · 403 the credential does not hold inbox:write · 404 the record or module is outside what it may reach · 422 invalid input (problems[]) · 409 conversation_not_found, already_open · 429 slow down
inbox.reply
Reply — Queues a message to the customer for delivery, stamping the first response when it is one.
Needs inbox:write · reaches outside (sends, publishes or charges)
https://app.getera.io/api/v1/conversations/{id}/actions/reply- Canonical address:
POST /api/v1/actions/inbox.reply
Input
| Field | Type | Required | Notes |
|---|---|---|---|
conversationId | uuid | yes | Filled from the path {id} · Conversation · id of conversation |
content | string | Message · max 150000 | |
contentType | string (enum) | Kind · Allowed values: text, input_text, input_textarea, input_email, input_select, cards, form, article, incoming_email, input_csat, integrations, sticker, voice_call | |
attachments | object / array | Attachments | |
cannedResponseId | uuid | Saved reply · id of canned_response | |
templateId | uuid | WhatsApp template · id of whatsapp_message_template | |
templateVariables | object / array | Template values | |
cc | object / array | Cc · Email only: more addresses that get the reply. | |
bcc | object / array | Bcc · Email only: addresses that get the reply unseen. | |
replyToMessageId | uuid | In reply to · id of message |
Preconditions
The action is refused with 409 and one of these codes:
| Code | Refused when |
|---|---|
conversation_not_found | The conversation must exist. |
empty_message | A message needs words or an attachment. |
too_many_attachments | A message carries at most fifteen attachments. |
too_many_messages | Too many messages on one conversation in a minute. |
canned_response_not_found | The saved reply must still exist. |
conversation_resolved | A resolved conversation only takes replies when its inbox allows them. |
Events it publishes: inbox.message_sent, inbox.reopened
What it writes: message (insert) — Writes the message; message_attachment (insert) — Attaches files and locations; conversation (update) — Clears the waiting clock and stamps the first reply; reporting_event (insert) — Writes the report clock reading (sql/126)
curl -X POST https://app.getera.io/api/v1/conversations/CONVERSATION_ID/actions/reply \
-H "Authorization: Bearer $ERA_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{"content":"Message"}'{
"ok": true,
"result": "…",
"events": [
"inbox.message_sent",
"inbox.reopened"
]
}import { createEra } from "@getera/sdk"
const era = createEra({ apiKey: process.env.ERA_API_KEY })
const res = await era.conversations.reply("CONVERSATION_ID", {
content: "Message"
})era conversations reply CONVERSATION_ID \
--content "Message"{
"name": "era__inbox__reply",
"arguments": {
"conversationId": "CONVERSATION_ID",
"content": "Message"
}
}Errors: 401 no or expired credential · 403 the credential does not hold inbox:write · 404 the record or module is outside what it may reach · 422 invalid input (problems[]) · 409 conversation_not_found, empty_message, too_many_attachments, too_many_messages, canned_response_not_found, conversation_resolved · 429 slow down
inbox.reset_agent_bot_credentials
Reset a bot’s token or secret — Issues a new API token, or changes the secret its events are signed with.
Needs inbox:manage · reaches outside (sends, publishes or charges) · asks a person to confirm when Era runs it
https://app.getera.io/api/v1/agent_bots/{id}/actions/reset_agent_bot_credentials- Canonical address:
POST /api/v1/actions/inbox.reset_agent_bot_credentials
Input
| Field | Type | Required | Notes |
|---|---|---|---|
id | uuid | yes | Filled from the path {id} · Bot · id of agent_bot |
what | string (enum) | yes | What to reset · Allowed values: token, secret |
Preconditions
The action is refused with 409 and one of these codes:
| Code | Refused when |
|---|---|
agent_bot_not_found | The bot must exist. |
Events it publishes: inbox.agent_bot_changed
What it writes: agent_bot (update) — Replaces the token hash, or moves the secret on a generation
curl -X POST https://app.getera.io/api/v1/agent_bots/AGENT_BOT_ID/actions/reset_agent_bot_credentials \
-H "Authorization: Bearer $ERA_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{"what":"token"}'{
"ok": true,
"result": "…",
"events": [
"inbox.agent_bot_changed"
]
}import { createEra } from "@getera/sdk"
const era = createEra({ apiKey: process.env.ERA_API_KEY })
const res = await era.agentBots.resetAgentBotCredentials("AGENT_BOT_ID", {
what: "token"
})era agent_bots reset_agent_bot_credentials AGENT_BOT_ID \
--what "token"{
"name": "era__inbox__reset_agent_bot_credentials",
"arguments": {
"id": "AGENT_BOT_ID",
"what": "token"
}
}Errors: 401 no or expired credential · 403 the credential does not hold inbox:manage · 404 the record or module is outside what it may reach · 422 invalid input (problems[]) · 409 agent_bot_not_found · 429 slow down
inbox.resolve
Resolve a conversation — Closes the conversation and stops its waiting clock.
Needs inbox:write · changes data
https://app.getera.io/api/v1/conversations/{id}/actions/resolve- Canonical address:
POST /api/v1/actions/inbox.resolve
Input
| Field | Type | Required | Notes |
|---|---|---|---|
id | uuid | yes | Filled from the path {id} · Conversation · id of conversation |
Preconditions
The action is refused with 409 and one of these codes:
| Code | Refused when |
|---|---|
conversation_not_found | The conversation must exist. |
already_resolved | It must not already be resolved. |
Events it publishes: inbox.resolved, inbox.message_sent
What it writes: conversation (update) — Resolves it and clears the clock; message (insert) — Queues the rating request, when the inbox asks for one; reporting_event (insert) — Writes the report clock reading (sql/126)
curl -X POST https://app.getera.io/api/v1/conversations/CONVERSATION_ID/actions/resolve \
-H "Authorization: Bearer $ERA_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{}'{
"ok": true,
"result": "…",
"events": [
"inbox.resolved",
"inbox.message_sent"
]
}import { createEra } from "@getera/sdk"
const era = createEra({ apiKey: process.env.ERA_API_KEY })
const res = await era.conversations.resolve("CONVERSATION_ID")era conversations resolve CONVERSATION_ID{
"name": "era__inbox__resolve",
"arguments": {
"id": "CONVERSATION_ID"
}
}Errors: 401 no or expired credential · 403 the credential does not hold inbox:write · 404 the record or module is outside what it may reach · 422 invalid input (problems[]) · 409 conversation_not_found, already_resolved · 429 slow down
inbox.retry_delivery
Retry sending — Sends a message that failed again, or pushes one that has been queued too long.
Needs inbox:write · changes data
https://app.getera.io/api/v1/messages/{id}/actions/retry_delivery- Canonical address:
POST /api/v1/actions/inbox.retry_delivery
Input
| Field | Type | Required | Notes |
|---|---|---|---|
id | uuid | yes | Filled from the path {id} · Message · id of message |
Preconditions
The action is refused with 409 and one of these codes:
| Code | Refused when |
|---|---|
message_not_found | The message must exist. |
message_not_outgoing | Only a message that goes out can be sent again. |
not_retryable | Only a failed message, or one queued for ten minutes, is retried. |
Events it publishes: inbox.delivery_retried
What it writes: message (update) — Queues it again under a new generation
curl -X POST https://app.getera.io/api/v1/messages/MESSAGE_ID/actions/retry_delivery \
-H "Authorization: Bearer $ERA_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{}'{
"ok": true,
"result": "…",
"events": [
"inbox.delivery_retried"
]
}import { createEra } from "@getera/sdk"
const era = createEra({ apiKey: process.env.ERA_API_KEY })
const res = await era.messages.retryDelivery("MESSAGE_ID")era messages retry_delivery MESSAGE_ID{
"name": "era__inbox__retry_delivery",
"arguments": {
"id": "MESSAGE_ID"
}
}Errors: 401 no or expired credential · 403 the credential does not hold inbox:write · 404 the record or module is outside what it may reach · 422 invalid input (problems[]) · 409 message_not_found, message_not_outgoing, not_retryable · 429 slow down
inbox.review_csat
Review a rating — Leaves the team’s notes on a customer’s rating.
Needs inbox:write · changes data
https://app.getera.io/api/v1/csat_survey_responses/{id}/actions/review_csat- Canonical address:
POST /api/v1/actions/inbox.review_csat
Input
| Field | Type | Required | Notes |
|---|---|---|---|
id | uuid | yes | Filled from the path {id} · Rating · id of csat_survey_response |
notes | string | Review notes · max 5000 |
Preconditions
The action is refused with 409 and one of these codes:
| Code | Refused when |
|---|---|
csat_not_found | The rating must exist. |
What it writes: csat_survey_response (update) — Writes the review notes
curl -X POST https://app.getera.io/api/v1/csat_survey_responses/CSAT_SURVEY_RESPONSE_ID/actions/review_csat \
-H "Authorization: Bearer $ERA_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{"notes":"Review notes"}'{
"ok": true,
"result": "…",
"events": []
}import { createEra } from "@getera/sdk"
const era = createEra({ apiKey: process.env.ERA_API_KEY })
const res = await era.csatSurveyResponses.reviewCsat("CSAT_SURVEY_RESPONSE_ID", {
notes: "Review notes"
})era csat_survey_responses review_csat CSAT_SURVEY_RESPONSE_ID \
--notes "Review notes"{
"name": "era__inbox__review_csat",
"arguments": {
"id": "CSAT_SURVEY_RESPONSE_ID",
"notes": "Review notes"
}
}Errors: 401 no or expired credential · 403 the credential does not hold inbox:write · 404 the record or module is outside what it may reach · 422 invalid input (problems[]) · 409 csat_not_found · 429 slow down
inbox.run_macro
Run a macro — Runs every step of a macro on the chosen conversations, all or none.
Needs inbox:write · changes data
https://app.getera.io/api/v1/messages/actions/run_macro- Canonical address:
POST /api/v1/actions/inbox.run_macro
Input
| Field | Type | Required | Notes |
|---|---|---|---|
id | uuid | yes | Macro · id of macro |
conversationIds | object / array | yes | Conversations |
Preconditions
The action is refused with 409 and one of these codes:
| Code | Refused when |
|---|---|
macro_not_found | The macro must exist and be visible to you. |
invalid_selection | Between one and a hundred conversations are chosen. |
invalid_macro_actions | A stored macro whose steps no longer parse is refused. |
Events it publishes: inbox.assigned, inbox.team_assigned, inbox.resolved, inbox.reopened, inbox.snoozed, inbox.priority_changed, inbox.message_sent, inbox.note_added, inbox.mentioned, inbox.user_mentioned, inbox.macro_executed, inbox.muted, inbox.transcript_requested
What it writes: conversation (update) — Applies the macro’s conversation steps; message (insert) — Writes the macro’s messages and notes; inbox_member (update) — Moves an assignee to the back of the queue; conversation_mention (upsert) — Records mentions in a note step; conversation_participant (insert) — Adds the people mentioned; reporting_event (insert) — Writes the report clock reading (sql/126)
curl -X POST https://app.getera.io/api/v1/messages/actions/run_macro \
-H "Authorization: Bearer $ERA_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{"id":"MACRO_ID","conversationIds":{}}'{
"ok": true,
"result": "…",
"events": [
"inbox.assigned",
"inbox.team_assigned",
"inbox.resolved",
"inbox.reopened",
"inbox.snoozed",
"inbox.priority_changed",
"inbox.message_sent",
"inbox.note_added",
"inbox.mentioned",
"inbox.user_mentioned",
"inbox.macro_executed",
"inbox.muted",
"inbox.transcript_requested"
]
}import { createEra } from "@getera/sdk"
const era = createEra({ apiKey: process.env.ERA_API_KEY })
const res = await era.messages.runMacro({
id: "MACRO_ID",
conversationIds: {}
})era messages run_macro --data '{"id":"MACRO_ID","conversationIds":{}}'{
"name": "era__inbox__run_macro",
"arguments": {
"id": "MACRO_ID",
"conversationIds": {}
}
}Errors: 401 no or expired credential · 403 the credential does not hold inbox:write · 404 the record or module is outside what it may reach · 422 invalid input (problems[]) · 409 macro_not_found, invalid_selection, invalid_macro_actions · 429 slow down
inbox.save_agent_bot
Register a bot — Adds or edits a bot that answers first and hands over when it cannot.
Needs inbox:manage · changes data
https://app.getera.io/api/v1/agent_bots/{id}/actions/save_agent_bot- REST alias:
PATCH /api/v1/agent_bots/{id} - Canonical address:
POST /api/v1/actions/inbox.save_agent_bot
Input
| Field | Type | Required | Notes |
|---|---|---|---|
id | uuid | Filled from the path {id} · Bot · id of agent_bot | |
name | string | yes | Name · max 200 |
description | string | Description · max 2000 | |
outgoingUrl | string | Where we send its events · max 2048 |
Preconditions
The action is refused with 409 and one of these codes:
| Code | Refused when |
|---|---|
agent_bot_not_found | A bot being changed must exist. |
bot_name_taken | Bot names are unique in a workspace. |
invalid_outgoing_url | The bot’s address is an http(s) URL of at most 2048 characters. |
Events it publishes: inbox.agent_bot_changed
What it writes: agent_bot (upsert) — Registers the bot; a new one gets its API token
curl -X POST https://app.getera.io/api/v1/agent_bots/AGENT_BOT_ID/actions/save_agent_bot \
-H "Authorization: Bearer $ERA_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{"name":"Name"}'{
"ok": true,
"result": "…",
"events": [
"inbox.agent_bot_changed"
]
}import { createEra } from "@getera/sdk"
const era = createEra({ apiKey: process.env.ERA_API_KEY })
const res = await era.agentBots.saveAgentBot("AGENT_BOT_ID", {
name: "Name"
})era agent_bots save_agent_bot AGENT_BOT_ID \
--name "Name"{
"name": "era__inbox__save_agent_bot",
"arguments": {
"id": "AGENT_BOT_ID",
"name": "Name"
}
}Errors: 401 no or expired credential · 403 the credential does not hold inbox:manage · 404 the record or module is outside what it may reach · 422 invalid input (problems[]) · 409 agent_bot_not_found, bot_name_taken, invalid_outgoing_url · 429 slow down
inbox.save_assignment_policy
Save a routing rule — Decides how conversations are shared out, and how much lands on one person.
Needs inbox:manage · changes data
https://app.getera.io/api/v1/assignment_policies/{id}/actions/save_assignment_policy- REST alias:
PATCH /api/v1/assignment_policies/{id} - Canonical address:
POST /api/v1/actions/inbox.save_assignment_policy
Input
| Field | Type | Required | Notes |
|---|---|---|---|
id | uuid | Filled from the path {id} · Rule · id of assignment_policy | |
name | string | yes | Name · max 200 |
description | string | Description · max 2000 | |
assignmentOrder | string (enum) | Order · Allowed values: round_robin, balanced | |
conversationPriority | string (enum) | Queue order · Allowed values: earliest_created, longest_waiting | |
fairDistributionLimit | number | Most per agent in the window · min 1 | |
fairDistributionWindow | number | Window (seconds) · min 1 | |
enabled | boolean | Enabled |
Preconditions
The action is refused with 409 and one of these codes:
| Code | Refused when |
|---|---|
invalid_policy_name | A rule needs a name. |
policy_name_taken | Rule names are unique within a workspace. |
policy_not_found | The rule being edited must exist. |
Events it publishes: inbox.assignment_policy_changed
What it writes: assignment_policy (upsert) — Writes the routing rule
curl -X POST https://app.getera.io/api/v1/assignment_policies/ASSIGNMENT_POLICY_ID/actions/save_assignment_policy \
-H "Authorization: Bearer $ERA_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{"name":"Name"}'{
"ok": true,
"result": "…",
"events": [
"inbox.assignment_policy_changed"
]
}import { createEra } from "@getera/sdk"
const era = createEra({ apiKey: process.env.ERA_API_KEY })
const res = await era.assignmentPolicies.saveAssignmentPolicy("ASSIGNMENT_POLICY_ID", {
name: "Name"
})era assignment_policies save_assignment_policy ASSIGNMENT_POLICY_ID \
--name "Name"{
"name": "era__inbox__save_assignment_policy",
"arguments": {
"id": "ASSIGNMENT_POLICY_ID",
"name": "Name"
}
}Errors: 401 no or expired credential · 403 the credential does not hold inbox:manage · 404 the record or module is outside what it may reach · 422 invalid input (problems[]) · 409 invalid_policy_name, policy_name_taken, policy_not_found · 429 slow down
inbox.save_canned_response
Save a reply — Adds or edits a reply agents can insert by typing its shortcut.
Needs inbox:manage · changes data
https://app.getera.io/api/v1/canned_responses/{id}/actions/save_canned_response- REST alias:
PATCH /api/v1/canned_responses/{id} - Canonical address:
POST /api/v1/actions/inbox.save_canned_response
Input
| Field | Type | Required | Notes |
|---|---|---|---|
id | uuid | Filled from the path {id} · Saved reply · id of canned_response | |
shortCode | string | yes | Shortcut · One word, typed after a slash in the composer. · max 60 |
content | string | yes | Reply · max 150000 |
Preconditions
The action is refused with 409 and one of these codes:
| Code | Refused when |
|---|---|
invalid_short_code | A shortcut is one word with no spaces. |
empty_canned_response | A saved reply needs something to say. |
short_code_taken | Shortcuts are unique within a workspace. |
canned_response_not_found | The reply being edited must exist. |
Events it publishes: inbox.canned_response_changed
What it writes: canned_response (upsert) — Writes the saved reply
curl -X POST https://app.getera.io/api/v1/canned_responses/CANNED_RESPONSE_ID/actions/save_canned_response \
-H "Authorization: Bearer $ERA_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{"shortCode":"Shortcut","content":"Reply"}'{
"ok": true,
"result": "…",
"events": [
"inbox.canned_response_changed"
]
}import { createEra } from "@getera/sdk"
const era = createEra({ apiKey: process.env.ERA_API_KEY })
const res = await era.cannedResponses.saveCannedResponse("CANNED_RESPONSE_ID", {
shortCode: "Shortcut",
content: "Reply"
})era canned_responses save_canned_response CANNED_RESPONSE_ID \
--short-code "Shortcut" \
--content "Reply"{
"name": "era__inbox__save_canned_response",
"arguments": {
"id": "CANNED_RESPONSE_ID",
"shortCode": "Shortcut",
"content": "Reply"
}
}Errors: 401 no or expired credential · 403 the credential does not hold inbox:manage · 404 the record or module is outside what it may reach · 422 invalid input (problems[]) · 409 invalid_short_code, empty_canned_response, short_code_taken, canned_response_not_found · 429 slow down
inbox.save_macro
Save a macro — Adds or edits a saved sequence of steps an agent runs on a conversation in one click.
Needs inbox:write · changes data
https://app.getera.io/api/v1/macros/{id}/actions/save_macro- REST alias:
PATCH /api/v1/macros/{id} - Canonical address:
POST /api/v1/actions/inbox.save_macro
Input
| Field | Type | Required | Notes |
|---|---|---|---|
id | uuid | Filled from the path {id} · Macro · id of macro | |
name | string | yes | Name · max 200 |
visibility | string (enum) | Who can use it · Allowed values: personal, global | |
actions | object / array | yes | Steps |
Preconditions
The action is refused with 409 and one of these codes:
| Code | Refused when |
|---|---|
invalid_macro_name | A macro needs a name. |
invalid_macro_actions | Every step is one this inbox can run, with its value. |
global_macro_needs_manage | Sharing a macro with everyone needs manage. |
macro_not_found | The macro being edited must exist and be visible to you. |
not_macro_owner | Only its author or a manager changes a macro. |
Events it publishes: inbox.macro_changed
What it writes: macro (upsert) — Writes the macro
curl -X POST https://app.getera.io/api/v1/macros/MACRO_ID/actions/save_macro \
-H "Authorization: Bearer $ERA_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{"name":"Name","actions":{}}'{
"ok": true,
"result": "…",
"events": [
"inbox.macro_changed"
]
}import { createEra } from "@getera/sdk"
const era = createEra({ apiKey: process.env.ERA_API_KEY })
const res = await era.macros.saveMacro("MACRO_ID", {
name: "Name",
actions: {}
})era macros save_macro MACRO_ID --data '{"name":"Name","actions":{}}'{
"name": "era__inbox__save_macro",
"arguments": {
"id": "MACRO_ID",
"name": "Name",
"actions": {}
}
}Errors: 401 no or expired credential · 403 the credential does not hold inbox:write · 404 the record or module is outside what it may reach · 422 invalid input (problems[]) · 409 invalid_macro_name, invalid_macro_actions, global_macro_needs_manage, macro_not_found, not_macro_owner · 429 slow down
inbox.save_reply_from_message
Save this as a reply — Copies a useful message into the shared reply library.
Needs inbox:manage · changes data
https://app.getera.io/api/v1/canned_responses/actions/save_reply_from_message- Canonical address:
POST /api/v1/actions/inbox.save_reply_from_message
Input
| Field | Type | Required | Notes |
|---|---|---|---|
id | uuid | yes | Message · id of message |
shortCode | string | yes | Shortcut · max 60 |
Preconditions
The action is refused with 409 and one of these codes:
| Code | Refused when |
|---|---|
message_not_found | The message must still exist. |
invalid_short_code | A shortcut is one word with no spaces. |
empty_canned_response | The message needs text to become a saved reply. |
short_code_taken | Shortcuts are unique within a workspace. |
Events it publishes: inbox.canned_response_changed
What it writes: canned_response (insert) — Adds the copied reply
curl -X POST https://app.getera.io/api/v1/canned_responses/actions/save_reply_from_message \
-H "Authorization: Bearer $ERA_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{"id":"MESSAGE_ID","shortCode":"Shortcut"}'{
"ok": true,
"result": "…",
"events": [
"inbox.canned_response_changed"
]
}import { createEra } from "@getera/sdk"
const era = createEra({ apiKey: process.env.ERA_API_KEY })
const res = await era.cannedResponses.saveReplyFromMessage({
id: "MESSAGE_ID",
shortCode: "Shortcut"
})era canned_responses save_reply_from_message \
--id "MESSAGE_ID" \
--short-code "Shortcut"{
"name": "era__inbox__save_reply_from_message",
"arguments": {
"id": "MESSAGE_ID",
"shortCode": "Shortcut"
}
}Errors: 401 no or expired credential · 403 the credential does not hold inbox:manage · 404 the record or module is outside what it may reach · 422 invalid input (problems[]) · 409 message_not_found, invalid_short_code, empty_canned_response, short_code_taken · 429 slow down
inbox.save_sla_policy
Save an SLA policy — Creates or changes a response-time target.
Needs inbox:manage · changes data
https://app.getera.io/api/v1/sla_policies/{id}/actions/save_sla_policy- REST alias:
PATCH /api/v1/sla_policies/{id} - Canonical address:
POST /api/v1/actions/inbox.save_sla_policy
Input
| Field | Type | Required | Notes |
|---|---|---|---|
id | uuid | Filled from the path {id} · Policy · id of sla_policy | |
name | string | yes | Name · max 200 |
description | string | Description · max 2000 | |
firstResponseSeconds | number | First response within (seconds) · min 0 | |
nextResponseSeconds | number | Next response within (seconds) · min 0 | |
resolutionSeconds | number | Resolution within (seconds) · min 0 | |
onlyDuringBusinessHours | boolean | Count business hours only |
Preconditions
The action is refused with 409 and one of these codes:
| Code | Refused when |
|---|---|
sla_threshold_required | At least one of the three targets is set. |
sla_name_taken | Policy names are unique in a workspace. |
sla_policy_not_found | A policy being changed must exist. |
Events it publishes: inbox.sla_policy_changed
What it writes: sla_policy (upsert) — Writes the policy
curl -X POST https://app.getera.io/api/v1/sla_policies/SLA_POLICY_ID/actions/save_sla_policy \
-H "Authorization: Bearer $ERA_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{"name":"Name"}'{
"ok": true,
"result": "…",
"events": [
"inbox.sla_policy_changed"
]
}import { createEra } from "@getera/sdk"
const era = createEra({ apiKey: process.env.ERA_API_KEY })
const res = await era.slaPolicies.saveSlaPolicy("SLA_POLICY_ID", {
name: "Name"
})era sla_policies save_sla_policy SLA_POLICY_ID \
--name "Name"{
"name": "era__inbox__save_sla_policy",
"arguments": {
"id": "SLA_POLICY_ID",
"name": "Name"
}
}Errors: 401 no or expired credential · 403 the credential does not hold inbox:manage · 404 the record or module is outside what it may reach · 422 invalid input (problems[]) · 409 sla_threshold_required, sla_name_taken, sla_policy_not_found · 429 slow down
inbox.save_team
Save a team — Adds or edits a team conversations can be routed to.
Needs inbox:manage · changes data
https://app.getera.io/api/v1/teams/{id}/actions/save_team- REST alias:
PATCH /api/v1/teams/{id} - Canonical address:
POST /api/v1/actions/inbox.save_team
Input
| Field | Type | Required | Notes |
|---|---|---|---|
id | uuid | Filled from the path {id} · Team · id of team | |
name | string | yes | Name · max 200 |
description | string | Description · max 2000 | |
allowAutoAssign | boolean | Assign automatically within the team · Off, conversations routed here wait for somebody to pick them up. |
Preconditions
The action is refused with 409 and one of these codes:
| Code | Refused when |
|---|---|
invalid_team_name | A team needs a name. |
team_name_taken | Team names are unique within a workspace. |
team_not_found | The team being edited must exist. |
Events it publishes: inbox.team_changed
What it writes: team (upsert) — Writes the team
curl -X POST https://app.getera.io/api/v1/teams/TEAM_ID/actions/save_team \
-H "Authorization: Bearer $ERA_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{"name":"Name"}'{
"ok": true,
"result": "…",
"events": [
"inbox.team_changed"
]
}import { createEra } from "@getera/sdk"
const era = createEra({ apiKey: process.env.ERA_API_KEY })
const res = await era.teams.saveTeam("TEAM_ID", {
name: "Name"
})era teams save_team TEAM_ID \
--name "Name"{
"name": "era__inbox__save_team",
"arguments": {
"id": "TEAM_ID",
"name": "Name"
}
}Errors: 401 no or expired credential · 403 the credential does not hold inbox:manage · 404 the record or module is outside what it may reach · 422 invalid input (problems[]) · 409 invalid_team_name, team_name_taken, team_not_found · 429 slow down
inbox.send_transcript
Email the transcript — Sends the whole public thread to an email address.
Needs inbox:write · reaches outside (sends, publishes or charges)
https://app.getera.io/api/v1/actions/inbox.send_transcriptInput
| Field | Type | Required | Notes |
|---|---|---|---|
id | uuid | yes | Conversation · id of conversation |
email | Send to |
Preconditions
The action is refused with 409 and one of these codes:
| Code | Refused when |
|---|---|
conversation_not_found | The conversation must exist. |
no_email_address | There must be an address — given, or the person’s own. |
transcript_empty | There must be something public in the thread. |
Events it publishes: inbox.transcript_requested
curl -X POST https://app.getera.io/api/v1/actions/inbox.send_transcript \
-H "Authorization: Bearer $ERA_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{"id":"CONVERSATION_ID"}'{
"ok": true,
"result": "…",
"events": [
"inbox.transcript_requested"
]
}import { createEra } from "@getera/sdk"
const era = createEra({ apiKey: process.env.ERA_API_KEY })
const res = await era.actions.run("inbox.send_transcript", {
id: "CONVERSATION_ID"
})era inbox send_transcript \
--id "CONVERSATION_ID"{
"name": "era__inbox__send_transcript",
"arguments": {
"id": "CONVERSATION_ID"
}
}Errors: 401 no or expired credential · 403 the credential does not hold inbox:write · 404 the record or module is outside what it may reach · 422 invalid input (problems[]) · 409 conversation_not_found, no_email_address, transcript_empty · 429 slow down
inbox.set_auto_resolve
Set automatic resolution — Resolves inactive open conversations in this inbox after the chosen number of hours.
Needs inbox:manage · changes data
https://app.getera.io/api/v1/inboxes/{id}/actions/set_auto_resolve- Canonical address:
POST /api/v1/actions/inbox.set_auto_resolve
Input
| Field | Type | Required | Notes |
|---|---|---|---|
inboxId | uuid | yes | Filled from the path {id} · Inbox · id of inbox |
hours | number | Hours of inactivity · min 1 · max 8760 | |
message | string | Activity note · max 2000 |
Preconditions
The action is refused with 409 and one of these codes:
| Code | Refused when |
|---|---|
inbox_not_found | The inbox must exist. |
Events it publishes: inbox.auto_resolve_changed
What it writes: inbox (update) — Writes the inactivity policy
curl -X POST https://app.getera.io/api/v1/inboxes/INBOXE_ID/actions/set_auto_resolve \
-H "Authorization: Bearer $ERA_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{"hours":1}'{
"ok": true,
"result": "…",
"events": [
"inbox.auto_resolve_changed"
]
}import { createEra } from "@getera/sdk"
const era = createEra({ apiKey: process.env.ERA_API_KEY })
const res = await era.inboxes.setAutoResolve("INBOXE_ID", {
hours: 1
})era inboxes set_auto_resolve INBOXE_ID \
--hours 1{
"name": "era__inbox__set_auto_resolve",
"arguments": {
"inboxId": "INBOXE_ID",
"hours": 1
}
}Errors: 401 no or expired credential · 403 the credential does not hold inbox:manage · 404 the record or module is outside what it may reach · 422 invalid input (problems[]) · 409 inbox_not_found · 429 slow down
inbox.set_inbox_bot
Choose the bot on an inbox — Makes a bot answer first on a channel, or takes it off.
Needs inbox:manage · changes data
https://app.getera.io/api/v1/agent_bot_inboxes/actions/set_inbox_bot- Canonical address:
POST /api/v1/actions/inbox.set_inbox_bot
Input
| Field | Type | Required | Notes |
|---|---|---|---|
inboxId | uuid | yes | Inbox · id of inbox |
agentBotId | uuid | Bot · id of agent_bot |
Preconditions
The action is refused with 409 and one of these codes:
| Code | Refused when |
|---|---|
inbox_not_found | The inbox must exist. |
agent_bot_not_found | The bot must exist. |
Events it publishes: inbox.agent_bot_changed
What it writes: agent_bot_inbox (upsert) — One active bot per inbox, or none
curl -X POST https://app.getera.io/api/v1/agent_bot_inboxes/actions/set_inbox_bot \
-H "Authorization: Bearer $ERA_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{"inboxId":"INBOX_ID"}'{
"ok": true,
"result": "…",
"events": [
"inbox.agent_bot_changed"
]
}import { createEra } from "@getera/sdk"
const era = createEra({ apiKey: process.env.ERA_API_KEY })
const res = await era.agentBotInboxes.setInboxBot({
inboxId: "INBOX_ID"
})era agent_bot_inboxes set_inbox_bot \
--inbox-id "INBOX_ID"{
"name": "era__inbox__set_inbox_bot",
"arguments": {
"inboxId": "INBOX_ID"
}
}Errors: 401 no or expired credential · 403 the credential does not hold inbox:manage · 404 the record or module is outside what it may reach · 422 invalid input (problems[]) · 409 inbox_not_found, agent_bot_not_found · 429 slow down
inbox.set_inbox_members
Set who works an inbox — Replaces the list of people who see and can be assigned this channel.
Needs inbox:manage · changes data
https://app.getera.io/api/v1/inbox_members/actions/set_inbox_members- Canonical address:
POST /api/v1/actions/inbox.set_inbox_members
Input
| Field | Type | Required | Notes |
|---|---|---|---|
id | uuid | yes | Inbox · id of inbox |
userIds | object / array | yes | People |
Preconditions
The action is refused with 409 and one of these codes:
| Code | Refused when |
|---|---|
inbox_not_found | The inbox must exist. |
not_a_member | Everyone listed must be an active member of the workspace. |
Events it publishes: inbox.members_changed
What it writes: inbox_member (upsert) — Replaces the rota
curl -X POST https://app.getera.io/api/v1/inbox_members/actions/set_inbox_members \
-H "Authorization: Bearer $ERA_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{"id":"INBOX_ID","userIds":{}}'{
"ok": true,
"result": "…",
"events": [
"inbox.members_changed"
]
}import { createEra } from "@getera/sdk"
const era = createEra({ apiKey: process.env.ERA_API_KEY })
const res = await era.inboxMembers.setInboxMembers({
id: "INBOX_ID",
userIds: {}
})era inbox_members set_inbox_members --data '{"id":"INBOX_ID","userIds":{}}'{
"name": "era__inbox__set_inbox_members",
"arguments": {
"id": "INBOX_ID",
"userIds": {}
}
}Errors: 401 no or expired credential · 403 the credential does not hold inbox:manage · 404 the record or module is outside what it may reach · 422 invalid input (problems[]) · 409 inbox_not_found, not_a_member · 429 slow down
inbox.set_inbox_policy
Set an inbox’s routing rule — Attaches one routing rule to a channel, or removes it.
Needs inbox:manage · changes data
https://app.getera.io/api/v1/inboxes/{id}/actions/set_inbox_policy- Canonical address:
POST /api/v1/actions/inbox.set_inbox_policy
Input
| Field | Type | Required | Notes |
|---|---|---|---|
inboxId | uuid | yes | Filled from the path {id} · Inbox · id of inbox |
policyId | uuid | Rule · id of assignment_policy |
Preconditions
The action is refused with 409 and one of these codes:
| Code | Refused when |
|---|---|
inbox_not_found | The inbox must exist. |
policy_not_found | The rule must exist. |
Events it publishes: inbox.assignment_policy_changed
What it writes: inbox (update) — Points the inbox at the rule
curl -X POST https://app.getera.io/api/v1/inboxes/INBOXE_ID/actions/set_inbox_policy \
-H "Authorization: Bearer $ERA_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{"policyId":"ASSIGNMENT_POLICY_ID"}'{
"ok": true,
"result": "…",
"events": [
"inbox.assignment_policy_changed"
]
}import { createEra } from "@getera/sdk"
const era = createEra({ apiKey: process.env.ERA_API_KEY })
const res = await era.inboxes.setInboxPolicy("INBOXE_ID", {
policyId: "ASSIGNMENT_POLICY_ID"
})era inboxes set_inbox_policy INBOXE_ID \
--policy-id "ASSIGNMENT_POLICY_ID"{
"name": "era__inbox__set_inbox_policy",
"arguments": {
"inboxId": "INBOXE_ID",
"policyId": "ASSIGNMENT_POLICY_ID"
}
}Errors: 401 no or expired credential · 403 the credential does not hold inbox:manage · 404 the record or module is outside what it may reach · 422 invalid input (problems[]) · 409 inbox_not_found, policy_not_found · 429 slow down
inbox.set_mailbox
Sign a mailbox in with Gmail or Outlook — Reads and answers an email inbox through a connected Gmail or Outlook account instead of a password server.
Needs inbox:manage · changes data
https://app.getera.io/api/v1/inboxes/{id}/actions/set_mailbox- Canonical address:
POST /api/v1/actions/inbox.set_mailbox
Input
| Field | Type | Required | Notes |
|---|---|---|---|
id | uuid | yes | Filled from the path {id} · Inbox · id of inbox |
credentialId | uuid | yes | Account |
Preconditions
The action is refused with 409 and one of these codes:
| Code | Refused when |
|---|---|
inbox_not_found | The inbox must exist. |
not_an_email_inbox | Only an email inbox has a mailbox to sign in. |
mailbox_connection_required | The account to sign in with is named. |
connection_not_found | The account is still connected in this workspace. |
mailbox_not_mail_app | The account is a Gmail or Outlook connection. |
connection_not_usable | The account is this person’s, the company’s, or shared with them. |
connection_inactive | The account is active, not paused or revoked. |
Events it publishes: inbox.channel_updated
What it writes: inbox (update) — Points the channel at the account and restarts its mail cursor
curl -X POST https://app.getera.io/api/v1/inboxes/INBOXE_ID/actions/set_mailbox \
-H "Authorization: Bearer $ERA_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{"credentialId":"CREDENTIAL_ID"}'{
"ok": true,
"result": "…",
"events": [
"inbox.channel_updated"
]
}import { createEra } from "@getera/sdk"
const era = createEra({ apiKey: process.env.ERA_API_KEY })
const res = await era.inboxes.setMailbox("INBOXE_ID", {
credentialId: "CREDENTIAL_ID"
})era inboxes set_mailbox INBOXE_ID \
--credential-id "CREDENTIAL_ID"{
"name": "era__inbox__set_mailbox",
"arguments": {
"id": "INBOXE_ID",
"credentialId": "CREDENTIAL_ID"
}
}Errors: 401 no or expired credential · 403 the credential does not hold inbox:manage · 404 the record or module is outside what it may reach · 422 invalid input (problems[]) · 409 inbox_not_found, not_an_email_inbox, mailbox_connection_required, connection_not_found, mailbox_not_mail_app, connection_not_usable, connection_inactive · 429 slow down
inbox.set_member_preference
Save Inbox preferences — Sets your availability, signature and sound preference for Inbox.
Needs inbox:read · changes data
https://app.getera.io/api/v1/inbox_member_preferences/actions/set_member_preference- Canonical address:
POST /api/v1/actions/inbox.set_member_preference
Input
| Field | Type | Required | Notes |
|---|---|---|---|
availability | string (enum) | Availability · Allowed values: online, busy, offline | |
messageSignature | string | Message signature · max 2000 | |
audioAlerts | boolean | Play audio alerts |
Events it publishes: inbox.member_preference_changed
What it writes: inbox_member_preference (upsert) — Writes the member preference
curl -X POST https://app.getera.io/api/v1/inbox_member_preferences/actions/set_member_preference \
-H "Authorization: Bearer $ERA_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{"availability":"online"}'{
"ok": true,
"result": "…",
"events": [
"inbox.member_preference_changed"
]
}import { createEra } from "@getera/sdk"
const era = createEra({ apiKey: process.env.ERA_API_KEY })
const res = await era.inboxMemberPreferences.setMemberPreference({
availability: "online"
})era inbox_member_preferences set_member_preference \
--availability "online"{
"name": "era__inbox__set_member_preference",
"arguments": {
"availability": "online"
}
}Errors: 401 no or expired credential · 403 the credential does not hold inbox:read · 404 the record or module is outside what it may reach · 422 invalid input (problems[]) · 429 slow down
inbox.set_priority
Set priority — Ranks the conversation, or leaves it unranked.
Needs inbox:write · changes data
https://app.getera.io/api/v1/conversations/{id}/actions/set_priority- Canonical address:
POST /api/v1/actions/inbox.set_priority
Input
| Field | Type | Required | Notes |
|---|---|---|---|
id | uuid | yes | Filled from the path {id} · Conversation · id of conversation |
priority | string (enum) | Priority · Allowed values: low, medium, high, urgent |
Preconditions
The action is refused with 409 and one of these codes:
| Code | Refused when |
|---|---|
conversation_not_found | The conversation must exist. |
Events it publishes: inbox.priority_changed
What it writes: conversation (update) — Sets the priority
curl -X POST https://app.getera.io/api/v1/conversations/CONVERSATION_ID/actions/set_priority \
-H "Authorization: Bearer $ERA_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{"priority":"low"}'{
"ok": true,
"result": "…",
"events": [
"inbox.priority_changed"
]
}import { createEra } from "@getera/sdk"
const era = createEra({ apiKey: process.env.ERA_API_KEY })
const res = await era.conversations.setPriority("CONVERSATION_ID", {
priority: "low"
})era conversations set_priority CONVERSATION_ID \
--priority "low"{
"name": "era__inbox__set_priority",
"arguments": {
"id": "CONVERSATION_ID",
"priority": "low"
}
}Errors: 401 no or expired credential · 403 the credential does not hold inbox:write · 404 the record or module is outside what it may reach · 422 invalid input (problems[]) · 409 conversation_not_found · 429 slow down
inbox.set_team_members
Set a team’s members — Replaces who is in the team.
Needs inbox:manage · changes data
https://app.getera.io/api/v1/team_members/actions/set_team_members- Canonical address:
POST /api/v1/actions/inbox.set_team_members
Input
| Field | Type | Required | Notes |
|---|---|---|---|
id | uuid | yes | Team · id of team |
userIds | object / array | yes | People |
Preconditions
The action is refused with 409 and one of these codes:
| Code | Refused when |
|---|---|
team_not_found | The team must exist. |
not_a_member | Everyone listed must be an active member of the workspace. |
Events it publishes: inbox.team_changed
What it writes: team_member (upsert) — Replaces the membership
curl -X POST https://app.getera.io/api/v1/team_members/actions/set_team_members \
-H "Authorization: Bearer $ERA_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{"id":"TEAM_ID","userIds":{}}'{
"ok": true,
"result": "…",
"events": [
"inbox.team_changed"
]
}import { createEra } from "@getera/sdk"
const era = createEra({ apiKey: process.env.ERA_API_KEY })
const res = await era.teamMembers.setTeamMembers({
id: "TEAM_ID",
userIds: {}
})era team_members set_team_members --data '{"id":"TEAM_ID","userIds":{}}'{
"name": "era__inbox__set_team_members",
"arguments": {
"id": "TEAM_ID",
"userIds": {}
}
}Errors: 401 no or expired credential · 403 the credential does not hold inbox:manage · 404 the record or module is outside what it may reach · 422 invalid input (problems[]) · 409 team_not_found, not_a_member · 429 slow down
inbox.set_working_hours
Set business hours — Sets when a channel answers, and what it says when nobody is there.
Needs inbox:manage · changes data
https://app.getera.io/api/v1/inboxes/{id}/actions/set_working_hours- Canonical address:
POST /api/v1/actions/inbox.set_working_hours
Input
| Field | Type | Required | Notes |
|---|---|---|---|
inboxId | uuid | yes | Filled from the path {id} · Inbox · id of inbox |
days | object / array | yes | Week |
workingHoursEnabled | boolean | Use business hours | |
outOfOfficeMessage | string | Out-of-office message · max 10000 | |
timezone | string | Timezone · max 100 |
Preconditions
The action is refused with 409 and one of these codes:
| Code | Refused when |
|---|---|
inbox_not_found | The inbox must exist. |
invalid_working_hours | Each day is closed, open all day, or open from one time to a later one. |
unknown_timezone | The timezone must be one the server knows. |
Events it publishes: inbox.working_hours_changed
What it writes: inbox_working_hour (upsert) — Writes the week; inbox (update) — Switches hours on or off and sets the out-of-office text
curl -X POST https://app.getera.io/api/v1/inboxes/INBOXE_ID/actions/set_working_hours \
-H "Authorization: Bearer $ERA_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{"days":{}}'{
"ok": true,
"result": "…",
"events": [
"inbox.working_hours_changed"
]
}import { createEra } from "@getera/sdk"
const era = createEra({ apiKey: process.env.ERA_API_KEY })
const res = await era.inboxes.setWorkingHours("INBOXE_ID", {
days: {}
})era inboxes set_working_hours INBOXE_ID --data '{"days":{}}'{
"name": "era__inbox__set_working_hours",
"arguments": {
"inboxId": "INBOXE_ID",
"days": {}
}
}Errors: 401 no or expired credential · 403 the credential does not hold inbox:manage · 404 the record or module is outside what it may reach · 422 invalid input (problems[]) · 409 inbox_not_found, invalid_working_hours, unknown_timezone · 429 slow down
inbox.snooze
Snooze a conversation — Puts the conversation down until a moment you choose.
Needs inbox:write · changes data
https://app.getera.io/api/v1/conversations/{id}/actions/snooze- Canonical address:
POST /api/v1/actions/inbox.snooze
Input
| Field | Type | Required | Notes |
|---|---|---|---|
id | uuid | yes | Filled from the path {id} · Conversation · id of conversation |
until | date-time | yes | Until |
Preconditions
The action is refused with 409 and one of these codes:
| Code | Refused when |
|---|---|
conversation_not_found | The conversation must exist. |
snooze_not_in_future | The snooze must end in the future. |
Events it publishes: inbox.snoozed
What it writes: conversation (update) — Snoozes it
curl -X POST https://app.getera.io/api/v1/conversations/CONVERSATION_ID/actions/snooze \
-H "Authorization: Bearer $ERA_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{"until":"2026-11-02T09:00:00Z"}'{
"ok": true,
"result": "…",
"events": [
"inbox.snoozed"
]
}import { createEra } from "@getera/sdk"
const era = createEra({ apiKey: process.env.ERA_API_KEY })
const res = await era.conversations.snooze("CONVERSATION_ID", {
until: "2026-11-02T09:00:00Z"
})era conversations snooze CONVERSATION_ID \
--until "2026-11-02T09:00:00Z"{
"name": "era__inbox__snooze",
"arguments": {
"id": "CONVERSATION_ID",
"until": "2026-11-02T09:00:00Z"
}
}Errors: 401 no or expired credential · 403 the credential does not hold inbox:write · 404 the record or module is outside what it may reach · 422 invalid input (problems[]) · 409 conversation_not_found, snooze_not_in_future · 429 slow down
inbox.submit_csat
Take a rating — Files the customer’s 1–5 rating and comment on a resolved conversation.
Needs inbox:handle · reaches outside (sends, publishes or charges)
https://app.getera.io/api/v1/csat_survey_responses/actions/submit_csat- Canonical address:
POST /api/v1/actions/inbox.submit_csat
Input
| Field | Type | Required | Notes |
|---|---|---|---|
conversationId | uuid | yes | Conversation · id of conversation |
rating | number | yes | Rating · min 1 · max 5 |
feedbackMessage | string | Comment · max 5000 |
Preconditions
The action is refused with 409 and one of these codes:
| Code | Refused when |
|---|---|
conversation_not_found | The conversation must exist. |
no_csat_survey | A rating needs a survey that asked for it. |
csat_locked | A survey locks fourteen days after it was sent. |
Events it publishes: inbox.csat_received
What it writes: csat_survey_response (upsert) — Files or replaces the rating; message (update) — Keeps the answer on the survey message
curl -X POST https://app.getera.io/api/v1/csat_survey_responses/actions/submit_csat \
-H "Authorization: Bearer $ERA_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{"conversationId":"CONVERSATION_ID","rating":1}'{
"ok": true,
"result": "…",
"events": [
"inbox.csat_received"
]
}import { createEra } from "@getera/sdk"
const era = createEra({ apiKey: process.env.ERA_API_KEY })
const res = await era.csatSurveyResponses.submitCsat({
conversationId: "CONVERSATION_ID",
rating: 1
})era csat_survey_responses submit_csat \
--conversation-id "CONVERSATION_ID" \
--rating 1{
"name": "era__inbox__submit_csat",
"arguments": {
"conversationId": "CONVERSATION_ID",
"rating": 1
}
}Errors: 401 no or expired credential · 403 the credential does not hold inbox:handle · 404 the record or module is outside what it may reach · 422 invalid input (problems[]) · 409 conversation_not_found, no_csat_survey, csat_locked · 429 slow down
inbox.sync_whatsapp_templates
Sync WhatsApp templates — Reads approved templates from the connected Meta business account without exposing its token.
Needs inbox:manage · changes data
https://app.getera.io/api/v1/whatsapp_message_templates/actions/sync_whatsapp_templates- Canonical address:
POST /api/v1/actions/inbox.sync_whatsapp_templates
Input
| Field | Type | Required | Notes |
|---|---|---|---|
inboxId | uuid | yes | WhatsApp inbox · id of inbox |
Preconditions
The action is refused with 409 and one of these codes:
| Code | Refused when |
|---|---|
inbox_not_found | The inbox must exist. |
whatsapp_not_connected | The inbox must be a connected WhatsApp channel with its business account. |
Events it publishes: inbox.whatsapp_templates_synced
What it writes: whatsapp_message_template (upsert) — Refreshes the approved template catalogue
curl -X POST https://app.getera.io/api/v1/whatsapp_message_templates/actions/sync_whatsapp_templates \
-H "Authorization: Bearer $ERA_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{"inboxId":"INBOX_ID"}'{
"ok": true,
"result": "…",
"events": [
"inbox.whatsapp_templates_synced"
]
}import { createEra } from "@getera/sdk"
const era = createEra({ apiKey: process.env.ERA_API_KEY })
const res = await era.whatsappMessageTemplates.syncWhatsappTemplates({
inboxId: "INBOX_ID"
})era whatsapp_message_templates sync_whatsapp_templates \
--inbox-id "INBOX_ID"{
"name": "era__inbox__sync_whatsapp_templates",
"arguments": {
"inboxId": "INBOX_ID"
}
}Errors: 401 no or expired credential · 403 the credential does not hold inbox:manage · 404 the record or module is outside what it may reach · 422 invalid input (problems[]) · 409 inbox_not_found, whatsapp_not_connected · 429 slow down
inbox.translate_message
Translate a message — Uses the workspace AI provider and stores the translation with the original message.
Needs inbox:write · changes data
https://app.getera.io/api/v1/messages/{id}/actions/translate_message- Canonical address:
POST /api/v1/actions/inbox.translate_message
Input
| Field | Type | Required | Notes |
|---|---|---|---|
id | uuid | yes | Filled from the path {id} · Message · id of message |
language | string | yes | Language · max 80 |
Preconditions
The action is refused with 409 and one of these codes:
| Code | Refused when |
|---|---|
message_not_found | The message must still exist. |
Events it publishes: inbox.message_translated
What it writes: message (update) — Stores the translation in message metadata
curl -X POST https://app.getera.io/api/v1/messages/MESSAGE_ID/actions/translate_message \
-H "Authorization: Bearer $ERA_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{"language":"Language"}'{
"ok": true,
"result": "…",
"events": [
"inbox.message_translated"
]
}import { createEra } from "@getera/sdk"
const era = createEra({ apiKey: process.env.ERA_API_KEY })
const res = await era.messages.translateMessage("MESSAGE_ID", {
language: "Language"
})era messages translate_message MESSAGE_ID \
--language "Language"{
"name": "era__inbox__translate_message",
"arguments": {
"id": "MESSAGE_ID",
"language": "Language"
}
}Errors: 401 no or expired credential · 403 the credential does not hold inbox:write · 404 the record or module is outside what it may reach · 422 invalid input (problems[]) · 409 message_not_found · 429 slow down
inbox.unmute
Unmute — Lets the next message reopen the conversation again.
Needs inbox:write · changes data
https://app.getera.io/api/v1/conversations/{id}/actions/unmute- Canonical address:
POST /api/v1/actions/inbox.unmute
Input
| Field | Type | Required | Notes |
|---|---|---|---|
id | uuid | yes | Filled from the path {id} · Conversation · id of conversation |
Preconditions
The action is refused with 409 and one of these codes:
| Code | Refused when |
|---|---|
conversation_not_found | The conversation must exist. |
not_muted | It must be muted. |
Events it publishes: inbox.unmuted
What it writes: conversation (update) — Clears the mute
curl -X POST https://app.getera.io/api/v1/conversations/CONVERSATION_ID/actions/unmute \
-H "Authorization: Bearer $ERA_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{}'{
"ok": true,
"result": "…",
"events": [
"inbox.unmuted"
]
}import { createEra } from "@getera/sdk"
const era = createEra({ apiKey: process.env.ERA_API_KEY })
const res = await era.conversations.unmute("CONVERSATION_ID")era conversations unmute CONVERSATION_ID{
"name": "era__inbox__unmute",
"arguments": {
"id": "CONVERSATION_ID"
}
}Errors: 401 no or expired credential · 403 the credential does not hold inbox:write · 404 the record or module is outside what it may reach · 422 invalid input (problems[]) · 409 conversation_not_found, not_muted · 429 slow down
inbox.update_inbox
Edit an inbox — Changes a channel’s greeting, hours, surveys and reply rules.
Needs inbox:manage · changes data
https://app.getera.io/api/v1/inboxes/{id}/actions/update_inbox- REST alias:
PATCH /api/v1/inboxes/{id} - Canonical address:
POST /api/v1/actions/inbox.update_inbox
Input
| Field | Type | Required | Notes |
|---|---|---|---|
id | uuid | yes | Filled from the path {id} · Inbox · id of inbox |
name | string | Name · max 200 | |
greetingEnabled | boolean | Send a greeting | |
greetingMessage | string | Greeting · max 2000 | |
outOfOfficeMessage | string | Out of office message · max 2000 | |
workingHoursEnabled | boolean | Only answer in working hours | |
timezone | string | Timezone · max 64 | |
enableAutoAssignment | boolean | Assign automatically | |
enableEmailCollect | boolean | Ask for an email address | |
csatSurveyEnabled | boolean | Ask for a rating when resolved | |
allowMessagesAfterResolved | boolean | Allow replies after resolving | |
lockToSingleConversation | boolean | One conversation per person | |
senderNameType | string (enum) | Sender name · Allowed values: friendly, professional | |
businessName | string | Business name · max 200 | |
helpSiteId | uuid | Help centre · id of site | |
channelConfig | object / array | Channel settings | |
csatConfig | object / array | Survey settings |
Preconditions
The action is refused with 409 and one of these codes:
| Code | Refused when |
|---|---|
inbox_not_found | The inbox must exist. |
invalid_csat_config | The survey’s display and label rule must be ones the source knows. |
unknown_timezone | The timezone must be one this server knows. |
Events it publishes: inbox.channel_updated
What it writes: inbox (update) — Updates the channel’s settings
curl -X POST https://app.getera.io/api/v1/inboxes/INBOXE_ID/actions/update_inbox \
-H "Authorization: Bearer $ERA_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{"name":"Name"}'{
"ok": true,
"result": "…",
"events": [
"inbox.channel_updated"
]
}import { createEra } from "@getera/sdk"
const era = createEra({ apiKey: process.env.ERA_API_KEY })
const res = await era.inboxes.updateInbox("INBOXE_ID", {
name: "Name"
})era inboxes update_inbox INBOXE_ID \
--name "Name"{
"name": "era__inbox__update_inbox",
"arguments": {
"id": "INBOXE_ID",
"name": "Name"
}
}Errors: 401 no or expired credential · 403 the credential does not hold inbox:manage · 404 the record or module is outside what it may reach · 422 invalid input (problems[]) · 409 inbox_not_found, invalid_csat_config, unknown_timezone · 429 slow down
Data models
AgentBotInbox
Table agent_bot_inbox · Resource agent_bot_inboxes
GET /api/v1/agent_bot_inboxesGET /api/v1/agent_bot_inboxes/{id}
| Column | Type | Nullable | Allowed values | References |
|---|---|---|---|---|
id | string (uuid) · uuid | |||
organization_id | string (uuid) · uuid | organization | ||
workspace_id | string (uuid) · uuid | workspace | ||
inbox_id | string (uuid) · uuid | inbox | ||
agent_bot_id | string (uuid) · uuid | agent_bot | ||
status | string · text | active, inactive | ||
created_at | string (date-time) · timestamptz | |||
updated_at | string (date-time) · timestamptz |
Primary key: id
AgentBot
Table agent_bot · Resource agent_bots
GET /api/v1/agent_botsGET /api/v1/agent_bots/{id}PATCH /api/v1/agent_bots/{id}→inbox.save_agent_botDELETE /api/v1/agent_bots/{id}→inbox.delete_agent_bot
| Column | Type | Nullable | Allowed values | References |
|---|---|---|---|---|
id | string (uuid) · uuid | |||
organization_id | string (uuid) · uuid | organization | ||
workspace_id | string (uuid) · uuid | workspace | ||
name | string · text | |||
description | string · text | yes | ||
bot_type | string · text | |||
outgoing_url | string · text | yes | ||
bot_config | object · jsonb | |||
created_at | string (date-time) · timestamptz | |||
updated_at | string (date-time) · timestamptz | |||
last_delivery_at | string (date-time) · timestamptz | yes | ||
last_delivery_error | string · text | yes |
Primary key: id
Checks:
CHECK ((bot_type = 'webhook'::text))CHECK (((last_delivery_error IS NULL) OR (length(last_delivery_error) <= 2000)))CHECK (((name = btrim(name)) AND (length(name) > 0)))CHECK (((outgoing_url IS NULL) OR ((length(outgoing_url) <= 2048) AND (outgoing_url ~ '^https?://'::text))))
AppliedSla
Table applied_sla · Resource applied_slas
GET /api/v1/applied_slasGET /api/v1/applied_slas/{id}
| Column | Type | Nullable | Allowed values | References |
|---|---|---|---|---|
id | string (uuid) · uuid | |||
organization_id | string (uuid) · uuid | organization | ||
workspace_id | string (uuid) · uuid | workspace | ||
conversation_id | string (uuid) · uuid | conversation | ||
sla_policy_id | string (uuid) · uuid | sla_policy | ||
sla_status | string · text | active, hit, missed, active_with_misses | ||
evaluated_at | string (date-time) · timestamptz | yes | ||
next_deadline_at | string (date-time) · timestamptz | yes | ||
next_deadline_type | string · text | yes | ||
created_at | string (date-time) · timestamptz | |||
updated_at | string (date-time) · timestamptz |
Primary key: id
Checks:
CHECK (((next_deadline_type IS NULL) OR (next_deadline_type = ANY (ARRAY['frt'::text, 'nrt'::text, 'rt'::text]))))
AssignmentPolicy
Table assignment_policy · Resource assignment_policies
GET /api/v1/assignment_policiesGET /api/v1/assignment_policies/{id}PATCH /api/v1/assignment_policies/{id}→inbox.save_assignment_policy
| Column | Type | Nullable | Allowed values | References |
|---|---|---|---|---|
id | string (uuid) · uuid | |||
organization_id | string (uuid) · uuid | organization | ||
workspace_id | string (uuid) · uuid | workspace | ||
name | string · text | |||
description | string · text | yes | ||
assignment_order | string · text | round_robin, balanced | ||
conversation_priority | string · text | earliest_created, longest_waiting | ||
fair_distribution_limit | integer · int4 | |||
fair_distribution_window | integer · int4 | |||
enabled | boolean · bool | |||
created_at | string (date-time) · timestamptz | |||
updated_at | string (date-time) · timestamptz |
Primary key: id
Checks:
CHECK ((fair_distribution_limit > 0))CHECK ((fair_distribution_window > 0))CHECK (((name = btrim(name)) AND (length(name) > 0)))
CannedResponse
Table canned_response · Resource canned_responses
GET /api/v1/canned_responsesGET /api/v1/canned_responses/{id}PATCH /api/v1/canned_responses/{id}→inbox.save_canned_responseDELETE /api/v1/canned_responses/{id}→inbox.delete_canned_response
| Column | Type | Nullable | Allowed values | References |
|---|---|---|---|---|
id | string (uuid) · uuid | |||
organization_id | string (uuid) · uuid | organization | ||
workspace_id | string (uuid) · uuid | workspace | ||
short_code | string · text | |||
content | string · text | |||
created_at | string (date-time) · timestamptz | |||
updated_at | string (date-time) · timestamptz |
Primary key: id
Checks:
CHECK (((length(btrim(content)) > 0) AND (length(content) <= 150000)))CHECK (((short_code = lower(btrim(short_code))) AND (length(short_code) > 0) AND (short_code !~ '\s'::text)))
ConversationMention
Table conversation_mention · Resource conversation_mentions
GET /api/v1/conversation_mentionsGET /api/v1/conversation_mentions/{id}
| Column | Type | Nullable | Allowed values | References |
|---|---|---|---|---|
id | string (uuid) · uuid | |||
organization_id | string (uuid) · uuid | organization | ||
workspace_id | string (uuid) · uuid | workspace | ||
conversation_id | string (uuid) · uuid | conversation | ||
user_id | string (uuid) · uuid | app_user | ||
message_id | string (uuid) · uuid | yes | message | |
mentioned_by | string (uuid) · uuid | yes | app_user | |
mentioned_at | string (date-time) · timestamptz | |||
created_at | string (date-time) · timestamptz | |||
updated_at | string (date-time) · timestamptz |
Primary key: id
ConversationParticipant
Table conversation_participant · Resource conversation_participants
GET /api/v1/conversation_participantsGET /api/v1/conversation_participants/{id}
| Column | Type | Nullable | Allowed values | References |
|---|---|---|---|---|
id | string (uuid) · uuid | |||
organization_id | string (uuid) · uuid | organization | ||
workspace_id | string (uuid) · uuid | workspace | ||
conversation_id | string (uuid) · uuid | conversation | ||
user_id | string (uuid) · uuid | app_user | ||
created_at | string (date-time) · timestamptz |
Primary key: id
Conversation
Table conversation · Resource conversations
GET /api/v1/conversationsGET /api/v1/conversations/{id}POST /api/v1/conversations→inbox.open_conversation
| Column | Type | Nullable | Allowed values | References |
|---|---|---|---|---|
id | string (uuid) · uuid | |||
organization_id | string (uuid) · uuid | organization | ||
workspace_id | string (uuid) · uuid | workspace | ||
inbox_id | string (uuid) · uuid | inbox | ||
customer_id | string (uuid) · uuid | customer | ||
customer_identity_id | string (uuid) · uuid | yes | customer_identity | |
status | string · text | open, resolved, pending, snoozed | ||
priority | string · text | yes | ||
assignee_user_id | string (uuid) · uuid | yes | app_user | |
assignee_agent_bot_id | string (uuid) · uuid | yes | agent_bot | |
assignee_set_at | string (date-time) · timestamptz | yes | ||
team_id | string (uuid) · uuid | yes | team | |
campaign_id | string (uuid) · uuid | yes | ||
display_no | string (int64) · int8 | |||
external_identifier | string · text | yes | ||
additional_attributes | object · jsonb | |||
custom_attributes | object · jsonb | |||
contact_last_seen_at | string (date-time) · timestamptz | yes | ||
agent_last_seen_at | string (date-time) · timestamptz | yes | ||
assignee_last_seen_at | string (date-time) · timestamptz | yes | ||
first_reply_created_at | string (date-time) · timestamptz | yes | ||
waiting_since | string (date-time) · timestamptz | yes | ||
snoozed_until | string (date-time) · timestamptz | yes | ||
last_activity_at | string (date-time) · timestamptz | |||
created_at | string (date-time) · timestamptz | |||
updated_at | string (date-time) · timestamptz | |||
muted_at | string (date-time) · timestamptz | yes | ||
era_mode | string · text | yes | ||
era_assigned_by | string (uuid) · uuid | yes | app_user | |
era_assigned_at | string (date-time) · timestamptz | yes |
Primary key: id
Checks:
CHECK (((assignee_user_id IS NULL) OR (assignee_agent_bot_id IS NULL)))CHECK (((era_mode IS NULL) OR (era_mode = ANY (ARRAY['draft'::text, 'answer'::text]))))CHECK (((priority IS NULL) OR (priority = ANY (ARRAY['low'::text, 'medium'::text, 'high'::text, 'urgent'::text]))))CHECK (((snoozed_until IS NULL) OR (status = 'snoozed'::text)))
CsatSurveyResponse
Table csat_survey_response · Resource csat_survey_responses
GET /api/v1/csat_survey_responsesGET /api/v1/csat_survey_responses/{id}
| Column | Type | Nullable | Allowed values | References |
|---|---|---|---|---|
id | string (uuid) · uuid | |||
organization_id | string (uuid) · uuid | organization | ||
workspace_id | string (uuid) · uuid | workspace | ||
conversation_id | string (uuid) · uuid | conversation | ||
message_id | string (uuid) · uuid | message | ||
customer_id | string (uuid) · uuid | yes | customer | |
assigned_agent_user_id | string (uuid) · uuid | yes | app_user | |
rating | integer · int4 | |||
feedback_message | string · text | yes | ||
csat_review_notes | string · text | yes | ||
review_notes_updated_at | string (date-time) · timestamptz | yes | ||
review_notes_updated_by | string (uuid) · uuid | yes | app_user | |
created_at | string (date-time) · timestamptz | |||
updated_at | string (date-time) · timestamptz |
Primary key: id
Checks:
CHECK (((csat_review_notes IS NULL) OR (length(csat_review_notes) <= 5000)))CHECK (((feedback_message IS NULL) OR (length(feedback_message) <= 5000)))CHECK (((rating >= 1) AND (rating <= 5)))
InboxMemberPreference
Table inbox_member_preference · Resource inbox_member_preferences
GET /api/v1/inbox_member_preferencesGET /api/v1/inbox_member_preferences/{id}
| Column | Type | Nullable | Allowed values | References |
|---|---|---|---|---|
id | string (uuid) · uuid | |||
organization_id | string (uuid) · uuid | organization | ||
workspace_id | string (uuid) · uuid | workspace | ||
user_id | string (uuid) · uuid | app_user | ||
availability_status | string · text | online, busy, offline | ||
message_signature | string · text | yes | ||
audio_alerts | boolean · bool | |||
updated_at | string (date-time) · timestamptz |
Primary key: id
Unique: workspace_id, user_id
Checks:
CHECK (((message_signature IS NULL) OR (length(message_signature) <= 2000)))
InboxMember
Table inbox_member · Resource inbox_members
GET /api/v1/inbox_membersGET /api/v1/inbox_members/{id}
| Column | Type | Nullable | Allowed values | References |
|---|---|---|---|---|
id | string (uuid) · uuid | |||
organization_id | string (uuid) · uuid | organization | ||
workspace_id | string (uuid) · uuid | workspace | ||
inbox_id | string (uuid) · uuid | inbox | ||
user_id | string (uuid) · uuid | app_user | ||
last_assigned_at | string (date-time) · timestamptz | yes | ||
created_at | string (date-time) · timestamptz |
Primary key: id
InboxWorkingHour
Table inbox_working_hour · Resource inbox_working_hours
GET /api/v1/inbox_working_hoursGET /api/v1/inbox_working_hours/{id}
| Column | Type | Nullable | Allowed values | References |
|---|---|---|---|---|
id | string (uuid) · uuid | |||
organization_id | string (uuid) · uuid | organization | ||
workspace_id | string (uuid) · uuid | workspace | ||
inbox_id | string (uuid) · uuid | inbox | ||
weekday | integer · int2 | |||
closed_all_day | boolean · bool | |||
open_all_day | boolean · bool | |||
opens_at | string (time) · time | yes | ||
closes_at | string (time) · time | yes | ||
created_at | string (date-time) · timestamptz | |||
updated_at | string (date-time) · timestamptz |
Primary key: id
Checks:
CHECK ((NOT (closed_all_day AND open_all_day)))CHECK ((closed_all_day OR open_all_day OR ((opens_at IS NOT NULL) AND (closes_at IS NOT NULL) AND (closes_at > opens_at))))CHECK (((weekday >= 0) AND (weekday <= 6)))
Inbox
Table inbox · Resource inboxes
GET /api/v1/inboxesGET /api/v1/inboxes/{id}POST /api/v1/inboxes→inbox.create_inboxPATCH /api/v1/inboxes/{id}→inbox.update_inbox
| Column | Type | Nullable | Allowed values | References |
|---|---|---|---|---|
id | string (uuid) · uuid | |||
organization_id | string (uuid) · uuid | organization | ||
workspace_id | string (uuid) · uuid | workspace | ||
name | string · text | |||
channel_type | string · text | web_widget, api, email, whatsapp, sms, twilio_sms, facebook_page, instagram, telegram, line, tiktok, twitter, voice | ||
identifier | string · text | yes | ||
channel_config | object · jsonb | |||
needs_reauth | boolean · bool | |||
is_active | boolean · bool | |||
enable_auto_assignment | boolean · bool | |||
assignment_policy_id | string (uuid) · uuid | yes | assignment_policy | |
greeting_enabled | boolean · bool | |||
greeting_message | string · text | yes | ||
email_address | string · text | yes | ||
working_hours_enabled | boolean · bool | |||
out_of_office_message | string · text | yes | ||
timezone | string · text | |||
enable_email_collect | boolean · bool | |||
csat_survey_enabled | boolean · bool | |||
csat_config | object · jsonb | |||
allow_messages_after_resolved | boolean · bool | |||
lock_to_single_conversation | boolean · bool | |||
sender_name_type | string · text | friendly, professional | ||
business_name | string · text | yes | ||
help_site_id | string (uuid) · uuid | yes | site | |
created_at | string (date-time) · timestamptz | |||
updated_at | string (date-time) · timestamptz | |||
health_status | string · text | unknown, healthy, degraded, reauth_required | ||
health_checked_at | string (date-time) · timestamptz | yes | ||
health_error | string · text | yes | ||
poll_state | object · jsonb | |||
auto_resolve_after_hours | integer · int4 | yes | ||
auto_resolve_message | string · text | yes | ||
health_details | object · jsonb |
Primary key: id
Checks:
CHECK (((auto_resolve_after_hours IS NULL) OR ((auto_resolve_after_hours >= 1) AND (auto_resolve_after_hours <= 8760))))CHECK (((health_error IS NULL) OR (length(health_error) <= 2000)))CHECK (((identifier IS NULL) OR ((identifier = btrim(identifier)) AND (length(identifier) > 0))))CHECK (((name = btrim(name)) AND (length(name) > 0)))CHECK ((jsonb_typeof(poll_state) = 'object'::text))CHECK (((channel_type <> ALL (ARRAY['sms'::text, 'twilio_sms'::text])) OR (COALESCE((channel_config ->> 'provider'::text), 'twilio'::text) = ANY (ARRAY['twilio'::text, 'netgsm'::text]))))
Macro
Table macro · Resource macros
GET /api/v1/macrosGET /api/v1/macros/{id}PATCH /api/v1/macros/{id}→inbox.save_macroDELETE /api/v1/macros/{id}→inbox.delete_macro
| Column | Type | Nullable | Allowed values | References |
|---|---|---|---|---|
id | string (uuid) · uuid | |||
organization_id | string (uuid) · uuid | organization | ||
workspace_id | string (uuid) · uuid | workspace | ||
name | string · text | |||
visibility | string · text | personal, global | ||
actions | object · jsonb | |||
created_by | string (uuid) · uuid | yes | app_user | |
updated_by | string (uuid) · uuid | yes | app_user | |
created_at | string (date-time) · timestamptz | |||
updated_at | string (date-time) · timestamptz |
Primary key: id
Checks:
CHECK ((jsonb_typeof(actions) = 'array'::text))CHECK (((name = btrim(name)) AND ((length(name) >= 1) AND (length(name) <= 200))))
MessageAttachment
Table message_attachment · Resource message_attachments
GET /api/v1/message_attachmentsGET /api/v1/message_attachments/{id}
| Column | Type | Nullable | Allowed values | References |
|---|---|---|---|---|
id | string (uuid) · uuid | |||
organization_id | string (uuid) · uuid | organization | ||
workspace_id | string (uuid) · uuid | workspace | ||
message_id | string (uuid) · uuid | message | ||
conversation_id | string (uuid) · uuid | conversation | ||
kind | string · text | image, audio, video, file, location, fallback, share, story_mention, contact, ig_reel, ig_post, ig_story, embed | ||
file_id | string (uuid) · uuid | yes | file | |
external_url | string · text | yes | ||
coordinates_lat | number · float8 | yes | ||
coordinates_long | number · float8 | yes | ||
fallback_title | string · text | yes | ||
meta | object · jsonb | |||
created_at | string (date-time) · timestamptz |
Primary key: id
Checks:
CHECK (((kind <> ALL (ARRAY['image'::text, 'audio'::text, 'video'::text, 'file'::text])) OR (file_id IS NOT NULL)))CHECK (((kind <> 'location'::text) OR ((coordinates_lat IS NOT NULL) AND (coordinates_long IS NOT NULL))))
Message
Table message · Resource messages
GET /api/v1/messagesGET /api/v1/messages/{id}DELETE /api/v1/messages/{id}→inbox.delete_message
| Column | Type | Nullable | Allowed values | References |
|---|---|---|---|---|
id | string (uuid) · uuid | |||
organization_id | string (uuid) · uuid | organization | ||
workspace_id | string (uuid) · uuid | workspace | ||
conversation_id | string (uuid) · uuid | conversation | ||
inbox_id | string (uuid) · uuid | inbox | ||
message_type | string · text | incoming, outgoing, activity, template | ||
content_type | string · text | text, input_text, input_textarea, input_email, input_select, cards, form, article, incoming_email, input_csat, integrations, sticker, voice_call | ||
status | string · text | queued, sent, delivered, read, failed | ||
content | string · text | yes | ||
processed_message_content | string · text | yes | ||
is_private | boolean · bool | |||
sender_kind | string · text | user, customer, agent_bot, system | ||
sender_user_id | string (uuid) · uuid | yes | app_user | |
sender_customer_id | string (uuid) · uuid | yes | customer | |
sender_agent_bot_id | string (uuid) · uuid | yes | agent_bot | |
source_id | string · text | yes | ||
content_attributes | object · jsonb | |||
external_source_ids | object · jsonb | |||
additional_attributes | object · jsonb | |||
sentiment | object · jsonb | |||
created_at | string (date-time) · timestamptz | |||
updated_at | string (date-time) · timestamptz | |||
deleted_at | string (date-time) · timestamptz | yes | ||
delivery_attempts | integer · int4 | |||
delivery_generation | integer · int4 | |||
delivery_error | string · text | yes | ||
status_changed_at | string (date-time) · timestamptz | yes |
Primary key: id
Checks:
CHECK (((content IS NULL) OR (length(content) <= 150000)))CHECK ((delivery_attempts >= 0))CHECK (((delivery_error IS NULL) OR (length(delivery_error) <= 2000)))CHECK (((delivery_error IS NULL) OR (status = ANY (ARRAY['queued'::text, 'failed'::text]))))CHECK ((delivery_generation >= 0))CHECK (((NOT is_private) OR (message_type = ANY (ARRAY['outgoing'::text, 'activity'::text]))))CHECK (((processed_message_content IS NULL) OR (length(processed_message_content) <= 150000)))CHECK (((status <> 'queued'::text) OR ((message_type = ANY (ARRAY['outgoing'::text, 'template'::text])) AND (NOT is_private))))CHECK ( CASE sender_kind WHEN 'user'::text THEN ((sender_user_id IS NOT NULL) AND (sender_customer_id IS NULL) AND (sender_agent_bot_id IS NULL)) WHEN 'customer'::text THEN ((sender_customer_id IS NOT NULL) AND (sender_user_id IS NULL) AND (sender_agent_bot_id IS NULL)) WHEN 'agent_bot'::text THEN ((sender_agent_bot_id IS NOT NULL) AND (sender_user_id IS NULL) AND (sender_customer_id IS NULL)) ELSE ((sender_user_id IS NULL) AND (sender_customer_id IS NULL) AND (sender_agent_bot_id IS NULL)) END)
ReportingEvent
Table reporting_event · Resource reporting_events
GET /api/v1/reporting_eventsGET /api/v1/reporting_events/{id}
| Column | Type | Nullable | Allowed values | References |
|---|---|---|---|---|
id | string (uuid) · uuid | |||
organization_id | string (uuid) · uuid | organization | ||
workspace_id | string (uuid) · uuid | workspace | ||
name | string · text | conversation_opened, first_response, reply_time, conversation_resolved, conversation_bot_resolved, conversation_bot_handoff | ||
value | integer · int4 | |||
value_in_business_hours | integer · int4 | yes | ||
inbox_id | string (uuid) · uuid | inbox | ||
conversation_id | string (uuid) · uuid | conversation | ||
user_id | string (uuid) · uuid | yes | app_user | |
team_id | string (uuid) · uuid | yes | team | |
event_start_time | string (date-time) · timestamptz | |||
event_end_time | string (date-time) · timestamptz | |||
created_at | string (date-time) · timestamptz |
Primary key: id
Checks:
CHECK ((event_end_time >= event_start_time))CHECK ((value >= 0))CHECK (((value_in_business_hours IS NULL) OR (value_in_business_hours >= 0)))
SlaEvent
Table sla_event · Resource sla_events
GET /api/v1/sla_eventsGET /api/v1/sla_events/{id}
| Column | Type | Nullable | Allowed values | References |
|---|---|---|---|---|
id | string (uuid) · uuid | |||
organization_id | string (uuid) · uuid | organization | ||
workspace_id | string (uuid) · uuid | workspace | ||
applied_sla_id | string (uuid) · uuid | applied_sla | ||
conversation_id | string (uuid) · uuid | conversation | ||
inbox_id | string (uuid) · uuid | inbox | ||
sla_policy_id | string (uuid) · uuid | sla_policy | ||
event_type | string · text | frt, nrt, rt | ||
meta | object · jsonb | |||
created_at | string (date-time) · timestamptz | |||
updated_at | string (date-time) · timestamptz |
Primary key: id
Checks:
CHECK ((jsonb_typeof(meta) = 'object'::text))
SlaPolicy
Table sla_policy · Resource sla_policies
GET /api/v1/sla_policiesGET /api/v1/sla_policies/{id}PATCH /api/v1/sla_policies/{id}→inbox.save_sla_policyDELETE /api/v1/sla_policies/{id}→inbox.delete_sla_policy
| Column | Type | Nullable | Allowed values | References |
|---|---|---|---|---|
id | string (uuid) · uuid | |||
organization_id | string (uuid) · uuid | organization | ||
workspace_id | string (uuid) · uuid | workspace | ||
name | string · text | |||
description | string · text | yes | ||
first_response_seconds | integer · int4 | yes | ||
next_response_seconds | integer · int4 | yes | ||
resolution_seconds | integer · int4 | yes | ||
only_during_business_hours | boolean · bool | |||
created_at | string (date-time) · timestamptz | |||
updated_at | string (date-time) · timestamptz |
Primary key: id
Checks:
CHECK (((first_response_seconds IS NOT NULL) OR (next_response_seconds IS NOT NULL) OR (resolution_seconds IS NOT NULL)))CHECK (((description IS NULL) OR (length(description) <= 2000)))CHECK (((first_response_seconds IS NULL) OR (first_response_seconds > 0)))CHECK (((name = btrim(name)) AND ((length(name) >= 1) AND (length(name) <= 200))))CHECK (((next_response_seconds IS NULL) OR (next_response_seconds > 0)))CHECK (((resolution_seconds IS NULL) OR (resolution_seconds > 0)))
TeamMember
Table team_member · Resource team_members
GET /api/v1/team_membersGET /api/v1/team_members/{id}
| Column | Type | Nullable | Allowed values | References |
|---|---|---|---|---|
id | string (uuid) · uuid | |||
organization_id | string (uuid) · uuid | organization | ||
workspace_id | string (uuid) · uuid | workspace | ||
team_id | string (uuid) · uuid | team | ||
user_id | string (uuid) · uuid | app_user | ||
created_at | string (date-time) · timestamptz |
Primary key: id
Team
Table team · Resource teams
GET /api/v1/teamsGET /api/v1/teams/{id}PATCH /api/v1/teams/{id}→inbox.save_teamDELETE /api/v1/teams/{id}→inbox.delete_team
| Column | Type | Nullable | Allowed values | References |
|---|---|---|---|---|
id | string (uuid) · uuid | |||
organization_id | string (uuid) · uuid | organization | ||
workspace_id | string (uuid) · uuid | workspace | ||
name | string · text | |||
description | string · text | yes | ||
allow_auto_assign | boolean · bool | |||
created_at | string (date-time) · timestamptz | |||
updated_at | string (date-time) · timestamptz |
Primary key: id
Checks:
CHECK (((name = btrim(name)) AND (length(name) > 0)))
WhatsappMessageTemplate
Table whatsapp_message_template · Resource whatsapp_message_templates
GET /api/v1/whatsapp_message_templatesGET /api/v1/whatsapp_message_templates/{id}
| Column | Type | Nullable | Allowed values | References |
|---|---|---|---|---|
id | string (uuid) · uuid | |||
organization_id | string (uuid) · uuid | organization | ||
workspace_id | string (uuid) · uuid | workspace | ||
inbox_id | string (uuid) · uuid | inbox | ||
provider_id | string · text | |||
name | string · text | |||
language | string · text | |||
category | string · text | yes | ||
status | string · text | approved, pending, rejected, paused, disabled | ||
body | string · text | |||
components | object · jsonb | |||
variable_count | integer · int4 | |||
synced_at | string (date-time) · timestamptz | |||
created_at | string (date-time) · timestamptz | |||
updated_at | string (date-time) · timestamptz |
Primary key: id
Unique: inbox_id, provider_id
Checks:
CHECK ((variable_count >= 0))
Events
Workflows and webhooks react to these events.
| Event | What happens | Payload |
|---|---|---|
inbox.agent_bot_changed | A bot was registered, changed, removed or put on an inbox. | id |
inbox.assigned | A conversation changed hands. | id, userId, automatic |
inbox.assignment_policy_changed | A routing rule changed. | id |
inbox.auto_resolve_changed | An inbox inactivity policy changed. | id, hours |
inbox.bot_handoff | A bot handed the conversation back to people. | id |
inbox.canned_response_changed | The saved replies changed. | id |
inbox.channel_connected | A channel was connected. | id, channelType |
inbox.channel_deactivated | A channel was taken out of service. | id |
inbox.channel_health_changed | A channel became reachable, unreachable, or needs reconnecting. | id, status |
inbox.channel_updated | A channel’s settings changed. | id |
inbox.conversation_opened | A conversation was opened. | id, inboxId, customerId, status |
inbox.csat_received | A customer rated a conversation. | id, responseId, rating, customerId |
inbox.customer_changed | A conversation was moved to another person. | id, customerId |
inbox.delivery_retried | Somebody asked for a failed reply to be sent again. | id, conversationId, generation |
inbox.era_assigned | A conversation was given to Era (to draft or to answer), or taken back. | id, mode |
inbox.macro_changed | A macro changed. | id |
inbox.macro_executed | A macro was run. | id, conversationIds |
inbox.member_preference_changed | An agent changed their Inbox presence or reply preferences. | id, userId |
inbox.members_changed | Who works an inbox changed. | id, count |
inbox.mentioned | Somebody was mentioned in a private note. | id, messageId, userIds |
inbox.message_deleted | A message was deleted. | id, conversationId |
inbox.message_delivered | A channel accepted, delivered or showed a reply. | id, conversationId, status |
inbox.message_edited | A sent message was corrected. | id, conversationId |
inbox.message_failed | A reply could not be delivered. | id, conversationId, reason |
inbox.message_received | A message arrived on a channel. | id, conversationId, inboxId, customerId |
inbox.message_sent | A reply was written and queued for delivery. | id, conversationId, firstReply, generation |
inbox.message_translated | A message translation was stored beside its original. | id, language |
inbox.muted | A conversation was muted. | id |
inbox.note_added | A private note was left. | id, conversationId |
inbox.participant_added | Somebody started following a conversation. | id, userId |
inbox.priority_changed | A conversation was ranked. | id, priority |
inbox.reopened | A conversation came back. | id |
inbox.resolved | A conversation was resolved. | id, customerId, csatRequested |
inbox.sla_applied | A conversation started being measured against an SLA. | id, appliedSlaId, policyId |
inbox.sla_closed | A resolved conversation’s SLA settled as hit or missed. | id, appliedSlaId, status |
inbox.sla_missed | A conversation missed an SLA target. | id, appliedSlaId, type, policy |
inbox.sla_policy_changed | An SLA policy changed. | id |
inbox.snoozed | A conversation was put down until later. | id, until |
inbox.team_assigned | A conversation moved team. | id, teamId, assigneeDropped |
inbox.team_changed | A team changed. | id |
inbox.transcript_requested | A conversation’s transcript was asked for by email. | id, email |
inbox.unmuted | A conversation was unmuted. | id |
inbox.user_mentioned | One person was mentioned in a private note — what a notification rule for them listens to. | id, messageId, userId, recipientUserId |
inbox.whatsapp_templates_synced | Approved WhatsApp templates were refreshed. | id, count |
inbox.working_hours_changed | A channel’s business hours changed. | id |