ReferenceReference by module

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

ResourceTableList and readPOSTPATCHDELETE
agent_bot_inboxesagent_bot_inbox/api/v1/agent_bot_inboxes
agent_botsagent_bot/api/v1/agent_botsinbox.save_agent_botinbox.delete_agent_bot
applied_slasapplied_sla/api/v1/applied_slas
assignment_policiesassignment_policy/api/v1/assignment_policiesinbox.save_assignment_policy
canned_responsescanned_response/api/v1/canned_responsesinbox.save_canned_responseinbox.delete_canned_response
conversation_mentionsconversation_mention/api/v1/conversation_mentions
conversation_participantsconversation_participant/api/v1/conversation_participants
conversationsconversation/api/v1/conversationsinbox.open_conversation
csat_survey_responsescsat_survey_response/api/v1/csat_survey_responses
inbox_member_preferencesinbox_member_preference/api/v1/inbox_member_preferences
inbox_membersinbox_member/api/v1/inbox_members
inbox_working_hoursinbox_working_hour/api/v1/inbox_working_hours
inboxesinbox/api/v1/inboxesinbox.create_inboxinbox.update_inbox
macrosmacro/api/v1/macrosinbox.save_macroinbox.delete_macro
message_attachmentsmessage_attachment/api/v1/message_attachments
messagesmessage/api/v1/messagesinbox.delete_message
reporting_eventsreporting_event/api/v1/reporting_events
sla_eventssla_event/api/v1/sla_events
sla_policiessla_policy/api/v1/sla_policiesinbox.save_sla_policyinbox.delete_sla_policy
team_membersteam_member/api/v1/team_members
teamsteam/api/v1/teamsinbox.save_teaminbox.delete_team
whatsapp_message_templateswhatsapp_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

ActionNeedsWhat it does
inbox.add_participantinbox:writeAdd a follower
inbox.add_private_noteinbox:commentAdd a private note
inbox.apply_slainbox:writeApply an SLA
inbox.assigninbox:writeAssign a conversation
inbox.assign_botinbox:writeHand a conversation to a bot
inbox.assign_erainbox:writeGive a conversation to Era
inbox.assign_teaminbox:writeMove a conversation to a team
inbox.auto_assigninbox:writeAssign automatically
inbox.bot_replyinbox:handleA bot replies
inbox.bot_set_statusinbox:handleA bot hands over or resolves
inbox.bulk_updateinbox:writeChange several conversations
inbox.change_customerinbox:writeMove a conversation to another person
inbox.create_inboxinbox:manageConnect a channel
inbox.deactivate_inboxinbox:manageTake a channel out of service
inbox.delete_agent_botinbox:manageRemove a bot
inbox.delete_canned_responseinbox:manageDelete a saved reply
inbox.delete_macroinbox:writeDelete a macro
inbox.delete_messageinbox:deleteDelete a message
inbox.delete_sla_policyinbox:manageDelete an SLA policy
inbox.delete_teaminbox:manageDelete a team
inbox.edit_messageinbox:writeEdit a message
inbox.evaluate_slainbox:handleCheck an SLA
inbox.ingestinbox:handleTake in an arriving message
inbox.mark_readinbox:readMark read
inbox.mark_unreadinbox:readMark unread
inbox.muteinbox:writeMute
inbox.open_conversationinbox:writeOpen a conversation
inbox.record_bot_deliveryinbox:handleRecord a bot delivery
inbox.record_channel_healthinbox:handleRecord a connection test
inbox.record_deliveryinbox:handleRecord what a channel said
inbox.record_poll_stateinbox:handleRecord the mailbox cursor
inbox.record_visitor_seeninbox:handleRecord that the visitor saw the thread
inbox.remove_participantinbox:writeRemove a follower
inbox.reopeninbox:writeReopen a conversation
inbox.replyinbox:writeReply
inbox.reset_agent_bot_credentialsinbox:manageReset a bot’s token or secret
inbox.resolveinbox:writeResolve a conversation
inbox.retry_deliveryinbox:writeRetry sending
inbox.review_csatinbox:writeReview a rating
inbox.run_macroinbox:writeRun a macro
inbox.save_agent_botinbox:manageRegister a bot
inbox.save_assignment_policyinbox:manageSave a routing rule
inbox.save_canned_responseinbox:manageSave a reply
inbox.save_macroinbox:writeSave a macro
inbox.save_reply_from_messageinbox:manageSave this as a reply
inbox.save_sla_policyinbox:manageSave an SLA policy
inbox.save_teaminbox:manageSave a team
inbox.send_transcriptinbox:writeEmail the transcript
inbox.set_auto_resolveinbox:manageSet automatic resolution
inbox.set_inbox_botinbox:manageChoose the bot on an inbox
inbox.set_inbox_membersinbox:manageSet who works an inbox
inbox.set_inbox_policyinbox:manageSet an inbox’s routing rule
inbox.set_mailboxinbox:manageSign a mailbox in with Gmail or Outlook
inbox.set_member_preferenceinbox:readSave Inbox preferences
inbox.set_priorityinbox:writeSet priority
inbox.set_team_membersinbox:manageSet a team’s members
inbox.set_working_hoursinbox:manageSet business hours
inbox.snoozeinbox:writeSnooze a conversation
inbox.submit_csatinbox:handleTake a rating
inbox.sync_whatsapp_templatesinbox:manageSync WhatsApp templates
inbox.translate_messageinbox:writeTranslate a message
inbox.unmuteinbox:writeUnmute
inbox.update_inboxinbox:manageEdit an inbox

inbox.add_participant

Add a follower — Lets somebody follow a conversation without being assigned it.

Needs inbox:write · changes data

POSThttps://app.getera.io/api/v1/conversation_participants/actions/add_participant
API key or OAuth token · inbox:write
  • Canonical address: POST /api/v1/actions/inbox.add_participant

Input

FieldTypeRequiredNotes
iduuidyesConversation · id of conversation
userIduuidyesPerson

Preconditions

The action is refused with 409 and one of these codes:

CodeRefused when
conversation_not_foundThe conversation must exist.
not_a_memberThey must be an active member of the workspace.
already_participatingThey 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"}'
Response
{
  "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"
tools/call
{
  "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

POSThttps://app.getera.io/api/v1/conversations/{id}/actions/add_private_note
API key or OAuth token · inbox:comment
  • Canonical address: POST /api/v1/actions/inbox.add_private_note

Input

FieldTypeRequiredNotes
conversationIduuidyesFilled from the path {id} · Conversation · id of conversation
contentstringMessage · max 150000
contentTypestring (enum)Kind · Allowed values: text, input_text, input_textarea, input_email, input_select, cards, form, article, incoming_email, input_csat, integrations, sticker, voice_call
attachmentsobject / arrayAttachments
cannedResponseIduuidSaved reply · id of canned_response
templateIduuidWhatsApp template · id of whatsapp_message_template
templateVariablesobject / arrayTemplate values
ccobject / arrayCc · Email only: more addresses that get the reply.
bccobject / arrayBcc · Email only: addresses that get the reply unseen.
replyToMessageIduuidIn reply to · id of message

Preconditions

The action is refused with 409 and one of these codes:

CodeRefused when
conversation_not_foundThe conversation must exist.
empty_messageA message needs words or an attachment.
too_many_attachmentsA message carries at most fifteen attachments.
too_many_messagesToo many messages on one conversation in a minute.
canned_response_not_foundThe saved reply must still exist.
mentioned_cannot_readEveryone 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"}'
Response
{
  "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"
tools/call
{
  "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

POSThttps://app.getera.io/api/v1/applied_slas/actions/apply_sla
API key or OAuth token · inbox:write
  • Canonical address: POST /api/v1/actions/inbox.apply_sla

Input

FieldTypeRequiredNotes
conversationIduuidyesConversation · id of conversation
policyIduuidyesPolicy · id of sla_policy

Preconditions

The action is refused with 409 and one of these codes:

CodeRefused when
conversation_not_foundThe conversation must exist.
sla_policy_not_foundThe policy must exist.
sla_already_appliedA 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"}'
Response
{
  "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"
tools/call
{
  "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

POSThttps://app.getera.io/api/v1/conversations/{id}/actions/assign
API key or OAuth token · inbox:write
  • Canonical address: POST /api/v1/actions/inbox.assign

Input

FieldTypeRequiredNotes
iduuidyesFilled from the path {id} · Conversation · id of conversation
userIduuidAgent

Preconditions

The action is refused with 409 and one of these codes:

CodeRefused when
conversation_not_foundThe conversation must exist.
not_a_memberThe agent must be an active member of the workspace.
not_an_inbox_memberThe agent must work that inbox.
not_in_conversation_teamAn 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"}'
Response
{
  "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"
tools/call
{
  "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

POSThttps://app.getera.io/api/v1/conversations/{id}/actions/assign_bot
API key or OAuth token · inbox:write
  • Canonical address: POST /api/v1/actions/inbox.assign_bot

Input

FieldTypeRequiredNotes
iduuidyesFilled from the path {id} · Conversation · id of conversation
agentBotIduuidBot · id of agent_bot

Preconditions

The action is refused with 409 and one of these codes:

CodeRefused when
conversation_not_foundThe conversation must exist.
bot_not_on_inboxThe 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"}'
Response
{
  "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"
tools/call
{
  "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

POSThttps://app.getera.io/api/v1/conversations/{id}/actions/assign_era
API key or OAuth token · inbox:write
  • Canonical address: POST /api/v1/actions/inbox.assign_era

Input

FieldTypeRequiredNotes
iduuidyesFilled from the path {id} · Conversation · id of conversation
modestring (enum)yesEra · Allowed values: draft, answer, off

Preconditions

The action is refused with 409 and one of these codes:

CodeRefused when
conversation_not_foundThe conversation must exist.
era_offEra (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"}'
Response
{
  "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"
tools/call
{
  "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

POSThttps://app.getera.io/api/v1/conversations/{id}/actions/assign_team
API key or OAuth token · inbox:write
  • Canonical address: POST /api/v1/actions/inbox.assign_team

Input

FieldTypeRequiredNotes
iduuidyesFilled from the path {id} · Conversation · id of conversation
teamIduuidTeam · id of team

Preconditions

The action is refused with 409 and one of these codes:

CodeRefused when
conversation_not_foundThe conversation must exist.
team_not_foundThe 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"}'
Response
{
  "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"
tools/call
{
  "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

POSThttps://app.getera.io/api/v1/conversations/{id}/actions/auto_assign
API key or OAuth token · inbox:write
  • Canonical address: POST /api/v1/actions/inbox.auto_assign

Input

FieldTypeRequiredNotes
iduuidyesFilled from the path {id} · Conversation · id of conversation

Preconditions

The action is refused with 409 and one of these codes:

CodeRefused when
conversation_not_foundThe conversation must exist.
inbox_not_foundThe inbox must exist.
auto_assignment_offAuto-assignment must be switched on for the inbox.
policy_disabledThe 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 '{}'
Response
{
  "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
tools/call
{
  "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)

POSThttps://app.getera.io/api/v1/conversations/{id}/actions/bot_reply
API key or OAuth token · inbox:handle
  • Canonical address: POST /api/v1/actions/inbox.bot_reply

Input

FieldTypeRequiredNotes
agentBotIduuidyesBot · id of agent_bot
conversationIduuidyesFilled from the path {id} · Conversation · id of conversation
contentstringMessage · max 150000
contentTypestringKind of message · max 40
contentAttributesobject / arrayOptions, cards or form

Preconditions

The action is refused with 409 and one of these codes:

CodeRefused when
agent_bot_not_foundThe bot must exist.
conversation_not_foundThe conversation must exist.
bot_not_on_inboxThe bot holds the conversation or answers on its inbox.
unsupported_content_typeA bot sends text, options, cards, forms, articles or input requests.
empty_messageA text message needs words.
conversation_resolvedA resolved conversation only takes messages where the inbox allows it.
too_many_messagesThe 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"}'
Response
{
  "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"
tools/call
{
  "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

POSThttps://app.getera.io/api/v1/conversations/{id}/actions/bot_set_status
API key or OAuth token · inbox:handle
  • Canonical address: POST /api/v1/actions/inbox.bot_set_status

Input

FieldTypeRequiredNotes
agentBotIduuidyesBot · id of agent_bot
conversationIduuidyesFilled from the path {id} · Conversation · id of conversation
statusstring (enum)yesStatus · Allowed values: open, resolved
reasonstringWhy · max 40

Preconditions

The action is refused with 409 and one of these codes:

CodeRefused when
agent_bot_not_foundThe bot must exist.
conversation_not_foundThe conversation must exist.
bot_not_on_inboxThe bot holds the conversation or answers on its inbox.
already_openA conversation already with people is not handed over again.
already_resolvedA 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"}'
Response
{
  "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"
tools/call
{
  "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

POSThttps://app.getera.io/api/v1/messages/actions/bulk_update
API key or OAuth token · inbox:write
  • Canonical address: POST /api/v1/actions/inbox.bulk_update

Input

FieldTypeRequiredNotes
idsobject / arrayyesConversations
statusstring (enum)Status · Allowed values: open, resolved, pending, snoozed
snoozedUntildate-timeSnooze until
assigneeUserIduuidAssign to
unassignbooleanReturn to the queue
teamIduuidTeam · id of team
clearTeambooleanTake off the team
prioritystring (enum)Priority · Allowed values: low, medium, high, urgent, none

Preconditions

The action is refused with 409 and one of these codes:

CodeRefused when
invalid_selectionBetween one and a hundred conversations are chosen.
nothing_to_changeSomething 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":{}}'
Response
{
  "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":{}}'
tools/call
{
  "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

POSThttps://app.getera.io/api/v1/conversations/{id}/actions/change_customer
API key or OAuth token · inbox:write
  • Canonical address: POST /api/v1/actions/inbox.change_customer

Input

FieldTypeRequiredNotes
iduuidyesFilled from the path {id} · Conversation · id of conversation
customerIduuidyesPerson · id of customer

Preconditions

The action is refused with 409 and one of these codes:

CodeRefused when
conversation_not_foundThe conversation must exist.
customer_not_foundThe person must already be on file.
customer_archivedAn 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"}'
Response
{
  "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"
tools/call
{
  "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

POSThttps://app.getera.io/api/v1/inboxes/actions/create_inbox
API key or OAuth token · inbox:manage
  • REST alias: POST /api/v1/inboxes
  • Canonical address: POST /api/v1/actions/inbox.create_inbox

Input

FieldTypeRequiredNotes
namestringyesName · max 200
channelTypestring (enum)yesChannel · Allowed values: web_widget, api, email, whatsapp, sms, twilio_sms, facebook_page, instagram, telegram, line, tiktok, twitter, voice
identifierstringNumber or account · The phone number, page id, bot name or token this channel answers on. · max 200
channelConfigobject / arrayChannel settings
credentialIduuidCredential
memberUserIdsobject / arrayWho works it

Preconditions

The action is refused with 409 and one of these codes:

CodeRefused when
identifier_takenA number or account may only serve one inbox.
channel_config_incompleteA channel must be given everything its provider needs before it is connected.
not_a_memberEveryone assigned to it must be an active member.
mailbox_connection_requiredA mailbox signed in with Gmail or Outlook names the account.
mailbox_not_mail_appThat account is a Gmail or Outlook connection.
connection_not_usableThat account is this person’s, the company’s, or shared with them.
connection_not_foundThe account is still connected in this workspace.
connection_inactiveThe 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"}'
Response
{
  "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"
tools/call
{
  "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

POSThttps://app.getera.io/api/v1/inboxes/{id}/actions/deactivate_inbox
API key or OAuth token · inbox:manage
  • Canonical address: POST /api/v1/actions/inbox.deactivate_inbox

Input

FieldTypeRequiredNotes
iduuidyesFilled from the path {id} · Inbox · id of inbox

Preconditions

The action is refused with 409 and one of these codes:

CodeRefused when
inbox_not_foundThe inbox must exist.
inbox_has_open_conversationsOpen 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 '{}'
Response
{
  "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
tools/call
{
  "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

POSThttps://app.getera.io/api/v1/agent_bots/{id}/actions/delete_agent_bot
API key or OAuth token · inbox:manage
  • REST alias: DELETE /api/v1/agent_bots/{id}
  • Canonical address: POST /api/v1/actions/inbox.delete_agent_bot

Input

FieldTypeRequiredNotes
iduuidyesFilled from the path {id} · Bot · id of agent_bot

Preconditions

The action is refused with 409 and one of these codes:

CodeRefused when
agent_bot_not_foundThe 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 '{}'
Response
{
  "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
tools/call
{
  "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

POSThttps://app.getera.io/api/v1/canned_responses/{id}/actions/delete_canned_response
API key or OAuth token · inbox:manage
  • REST alias: DELETE /api/v1/canned_responses/{id}
  • Canonical address: POST /api/v1/actions/inbox.delete_canned_response

Input

FieldTypeRequiredNotes
iduuidyesFilled from the path {id} · Saved reply · id of canned_response

Preconditions

The action is refused with 409 and one of these codes:

CodeRefused when
canned_response_not_foundThe 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 '{}'
Response
{
  "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
tools/call
{
  "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

POSThttps://app.getera.io/api/v1/macros/{id}/actions/delete_macro
API key or OAuth token · inbox:write
  • REST alias: DELETE /api/v1/macros/{id}
  • Canonical address: POST /api/v1/actions/inbox.delete_macro

Input

FieldTypeRequiredNotes
iduuidyesFilled from the path {id} · Macro · id of macro

Preconditions

The action is refused with 409 and one of these codes:

CodeRefused when
macro_not_foundThe macro must exist and be visible to you.
not_macro_ownerOnly 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 '{}'
Response
{
  "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
tools/call
{
  "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

POSThttps://app.getera.io/api/v1/messages/{id}/actions/delete_message
API key or OAuth token · inbox:delete
  • REST alias: DELETE /api/v1/messages/{id}
  • Canonical address: POST /api/v1/actions/inbox.delete_message

Input

FieldTypeRequiredNotes
iduuidyesFilled from the path {id} · Message · id of message

Preconditions

The action is refused with 409 and one of these codes:

CodeRefused when
message_not_foundThe 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 '{}'
Response
{
  "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
tools/call
{
  "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

POSThttps://app.getera.io/api/v1/sla_policies/{id}/actions/delete_sla_policy
API key or OAuth token · inbox:manage
  • REST alias: DELETE /api/v1/sla_policies/{id}
  • Canonical address: POST /api/v1/actions/inbox.delete_sla_policy

Input

FieldTypeRequiredNotes
iduuidyesFilled from the path {id} · Policy · id of sla_policy

Preconditions

The action is refused with 409 and one of these codes:

CodeRefused when
sla_policy_not_foundThe policy must exist.
sla_policy_in_useA 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 '{}'
Response
{
  "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
tools/call
{
  "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

POSThttps://app.getera.io/api/v1/teams/{id}/actions/delete_team
API key or OAuth token · inbox:manage
  • REST alias: DELETE /api/v1/teams/{id}
  • Canonical address: POST /api/v1/actions/inbox.delete_team

Input

FieldTypeRequiredNotes
iduuidyesFilled from the path {id} · Team · id of team
forcebooleanDelete anyway

Preconditions

The action is refused with 409 and one of these codes:

CodeRefused when
team_not_foundThe team must exist.
team_has_open_conversationsOpen 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}'
Response
{
  "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
tools/call
{
  "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)

POSThttps://app.getera.io/api/v1/messages/{id}/actions/edit_message
API key or OAuth token · inbox:write
  • Canonical address: POST /api/v1/actions/inbox.edit_message

Input

FieldTypeRequiredNotes
iduuidyesFilled from the path {id} · Message · id of message
contentstringyesMessage · max 150000

Preconditions

The action is refused with 409 and one of these codes:

CodeRefused when
message_not_foundThe message must exist.
message_not_editableOnly an outgoing message can be edited.
empty_messageA 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"}'
Response
{
  "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"
tools/call
{
  "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

POSThttps://app.getera.io/api/v1/applied_slas/{id}/actions/evaluate_sla
API key or OAuth token · inbox:handle
  • Canonical address: POST /api/v1/actions/inbox.evaluate_sla

Input

FieldTypeRequiredNotes
iduuidyesFilled from the path {id} · Applied SLA · id of applied_sla

Preconditions

The action is refused with 409 and one of these codes:

CodeRefused when
applied_sla_not_foundThe 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 '{}'
Response
{
  "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
tools/call
{
  "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

POSThttps://app.getera.io/api/v1/conversations/actions/ingest
API key or OAuth token · inbox:handle
  • Canonical address: POST /api/v1/actions/inbox.ingest

Input

FieldTypeRequiredNotes
inboxIduuidyesInbox · id of inbox
customerIduuidyesPerson · id of customer
sourceIdstringyesThe provider’s id for this message · What makes a redelivered webhook one message rather than two. · max 200
contentstringWhat they said · max 150000
senderNamestringName at the provider · max 200
customerIdentityIduuidChannel address · id of customer_identity
channelAddressstringChannel address value · The customer_identity value of kind channel — the thread is keyed on it. · max 300
contentTypestring (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
attachmentsobject / arrayAttachments
inReplyToobject / arrayReplies to (email ids)
newThreadbooleanStart a new thread unless replying

Preconditions

The action is refused with 409 and one of these codes:

CodeRefused when
inbox_not_foundThe inbox must exist.
inbox_inactiveThe channel must still be in service.
no_source_idAn arriving message must carry the provider’s id for it.
empty_messageA message needs words or an attachment.
malformed_messageAttachments and replies-to are lists.
duplicate_messageThe same delivery is only ever filed once.
customer_not_foundThe person must already be on file.
customer_archivedAn 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"}'
Response
{
  "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"
tools/call
{
  "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

POSThttps://app.getera.io/api/v1/conversations/{id}/actions/mark_read
API key or OAuth token · inbox:read
  • Canonical address: POST /api/v1/actions/inbox.mark_read

Input

FieldTypeRequiredNotes
iduuidyesFilled from the path {id} · Conversation · id of conversation

Preconditions

The action is refused with 409 and one of these codes:

CodeRefused when
conversation_not_foundThe 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 '{}'
Response
{
  "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
tools/call
{
  "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

POSThttps://app.getera.io/api/v1/conversations/{id}/actions/mark_unread
API key or OAuth token · inbox:read
  • Canonical address: POST /api/v1/actions/inbox.mark_unread

Input

FieldTypeRequiredNotes
iduuidyesFilled from the path {id} · Conversation · id of conversation

Preconditions

The action is refused with 409 and one of these codes:

CodeRefused when
conversation_not_foundThe 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 '{}'
Response
{
  "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
tools/call
{
  "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

POSThttps://app.getera.io/api/v1/conversations/{id}/actions/mute
API key or OAuth token · inbox:write
  • Canonical address: POST /api/v1/actions/inbox.mute

Input

FieldTypeRequiredNotes
iduuidyesFilled from the path {id} · Conversation · id of conversation

Preconditions

The action is refused with 409 and one of these codes:

CodeRefused when
conversation_not_foundThe conversation must exist.
already_mutedIt 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 '{}'
Response
{
  "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
tools/call
{
  "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

POSThttps://app.getera.io/api/v1/conversations/actions/open_conversation
API key or OAuth token · inbox:write
  • REST alias: POST /api/v1/conversations
  • Canonical address: POST /api/v1/actions/inbox.open_conversation

Input

FieldTypeRequiredNotes
inboxIduuidyesInbox · id of inbox
customerIduuidyesPerson · id of customer
customerIdentityIduuidChannel address · id of customer_identity
teamIduuidTeam · id of team
prioritystring (enum)Priority · Allowed values: low, medium, high, urgent
externalIdentifierstringChannel reference · max 200

Preconditions

The action is refused with 409 and one of these codes:

CodeRefused when
inbox_not_foundThe inbox must exist.
inbox_inactiveThe channel must still be in service.
customer_not_foundThe person must already be on file.
customer_archivedAn archived person cannot be written to.
identity_not_theirsThe channel address must belong to that person.
conversation_already_openAn inbox locked to one conversation per person refuses a second.
team_not_foundThe 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"}'
Response
{
  "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"
tools/call
{
  "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

Run by Era itself (machine-only). Listed so the reference is complete; an API key or a token is refused (403 machine_only).
POSThttps://app.getera.io/api/v1/agent_bots/{id}/actions/record_bot_delivery
API key or OAuth token · inbox:handle
  • Canonical address: POST /api/v1/actions/inbox.record_bot_delivery

Input

FieldTypeRequiredNotes
agentBotIduuidyesFilled from the path {id} · Bot · id of agent_bot
errorstringWhat went wrong · max 2000

Preconditions

The action is refused with 409 and one of these codes:

CodeRefused when
agent_bot_not_foundThe 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

POSThttps://app.getera.io/api/v1/inboxes/{id}/actions/record_channel_health
API key or OAuth token · inbox:handle
  • Canonical address: POST /api/v1/actions/inbox.record_channel_health

Input

FieldTypeRequiredNotes
inboxIduuidyesFilled from the path {id} · Inbox · id of inbox
statusstring (enum)yesStatus · Allowed values: healthy, degraded, reauth_required
errorstringReason · max 2000
detailsobject / arrayProvider details

Preconditions

The action is refused with 409 and one of these codes:

CodeRefused when
inbox_not_foundThe 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"}'
Response
{
  "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"
tools/call
{
  "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

POSThttps://app.getera.io/api/v1/messages/{id}/actions/record_delivery
API key or OAuth token · inbox:handle
  • Canonical address: POST /api/v1/actions/inbox.record_delivery

Input

FieldTypeRequiredNotes
messageIduuidFilled from the path {id} · Message · id of message
inboxIduuidInbox · id of inbox
sourceIdstringProvider message id · max 300
statusstring (enum)yesStatus · Allowed values: queued, sent, delivered, read, failed
generationnumberAttempt generation · min 0
providerMessageIdstringProvider message id · max 300
errorstringReason · max 2000
attemptsnumberAttempts · min 0 · max 100

Preconditions

The action is refused with 409 and one of these codes:

CodeRefused when
message_not_foundThe message must exist.
message_not_outgoingOnly 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"}'
Response
{
  "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"
tools/call
{
  "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

POSThttps://app.getera.io/api/v1/inboxes/{id}/actions/record_poll_state
API key or OAuth token · inbox:handle
  • Canonical address: POST /api/v1/actions/inbox.record_poll_state

Input

FieldTypeRequiredNotes
inboxIduuidyesFilled from the path {id} · Inbox · id of inbox
pollStateobject / arrayyesCursor

Preconditions

The action is refused with 409 and one of these codes:

CodeRefused when
inbox_not_foundThe inbox must exist.
invalid_poll_stateThe 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":{}}'
Response
{
  "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":{}}'
tools/call
{
  "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

POSThttps://app.getera.io/api/v1/conversations/{id}/actions/record_visitor_seen
API key or OAuth token · inbox:handle
  • Canonical address: POST /api/v1/actions/inbox.record_visitor_seen

Input

FieldTypeRequiredNotes
conversationIduuidyesFilled from the path {id} · Conversation · id of conversation

Preconditions

The action is refused with 409 and one of these codes:

CodeRefused when
conversation_not_foundThe 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 '{}'
Response
{
  "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
tools/call
{
  "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

POSThttps://app.getera.io/api/v1/conversation_participants/actions/remove_participant
API key or OAuth token · inbox:write
  • Canonical address: POST /api/v1/actions/inbox.remove_participant

Input

FieldTypeRequiredNotes
iduuidyesConversation · id of conversation
userIduuidyesPerson

Preconditions

The action is refused with 409 and one of these codes:

CodeRefused when
conversation_not_foundThe conversation must exist.
not_participatingThey 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"}'
Response
{
  "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"
tools/call
{
  "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

POSThttps://app.getera.io/api/v1/conversations/{id}/actions/reopen
API key or OAuth token · inbox:write
  • Canonical address: POST /api/v1/actions/inbox.reopen

Input

FieldTypeRequiredNotes
iduuidyesFilled from the path {id} · Conversation · id of conversation

Preconditions

The action is refused with 409 and one of these codes:

CodeRefused when
conversation_not_foundThe conversation must exist.
already_openIt 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 '{}'
Response
{
  "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
tools/call
{
  "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)

POSThttps://app.getera.io/api/v1/conversations/{id}/actions/reply
API key or OAuth token · inbox:write
  • Canonical address: POST /api/v1/actions/inbox.reply

Input

FieldTypeRequiredNotes
conversationIduuidyesFilled from the path {id} · Conversation · id of conversation
contentstringMessage · max 150000
contentTypestring (enum)Kind · Allowed values: text, input_text, input_textarea, input_email, input_select, cards, form, article, incoming_email, input_csat, integrations, sticker, voice_call
attachmentsobject / arrayAttachments
cannedResponseIduuidSaved reply · id of canned_response
templateIduuidWhatsApp template · id of whatsapp_message_template
templateVariablesobject / arrayTemplate values
ccobject / arrayCc · Email only: more addresses that get the reply.
bccobject / arrayBcc · Email only: addresses that get the reply unseen.
replyToMessageIduuidIn reply to · id of message

Preconditions

The action is refused with 409 and one of these codes:

CodeRefused when
conversation_not_foundThe conversation must exist.
empty_messageA message needs words or an attachment.
too_many_attachmentsA message carries at most fifteen attachments.
too_many_messagesToo many messages on one conversation in a minute.
canned_response_not_foundThe saved reply must still exist.
conversation_resolvedA 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"}'
Response
{
  "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"
tools/call
{
  "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

POSThttps://app.getera.io/api/v1/agent_bots/{id}/actions/reset_agent_bot_credentials
API key or OAuth token · inbox:manage
  • Canonical address: POST /api/v1/actions/inbox.reset_agent_bot_credentials

Input

FieldTypeRequiredNotes
iduuidyesFilled from the path {id} · Bot · id of agent_bot
whatstring (enum)yesWhat to reset · Allowed values: token, secret

Preconditions

The action is refused with 409 and one of these codes:

CodeRefused when
agent_bot_not_foundThe 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"}'
Response
{
  "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"
tools/call
{
  "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

POSThttps://app.getera.io/api/v1/conversations/{id}/actions/resolve
API key or OAuth token · inbox:write
  • Canonical address: POST /api/v1/actions/inbox.resolve

Input

FieldTypeRequiredNotes
iduuidyesFilled from the path {id} · Conversation · id of conversation

Preconditions

The action is refused with 409 and one of these codes:

CodeRefused when
conversation_not_foundThe conversation must exist.
already_resolvedIt 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 '{}'
Response
{
  "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
tools/call
{
  "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

POSThttps://app.getera.io/api/v1/messages/{id}/actions/retry_delivery
API key or OAuth token · inbox:write
  • Canonical address: POST /api/v1/actions/inbox.retry_delivery

Input

FieldTypeRequiredNotes
iduuidyesFilled from the path {id} · Message · id of message

Preconditions

The action is refused with 409 and one of these codes:

CodeRefused when
message_not_foundThe message must exist.
message_not_outgoingOnly a message that goes out can be sent again.
not_retryableOnly 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 '{}'
Response
{
  "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
tools/call
{
  "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

POSThttps://app.getera.io/api/v1/csat_survey_responses/{id}/actions/review_csat
API key or OAuth token · inbox:write
  • Canonical address: POST /api/v1/actions/inbox.review_csat

Input

FieldTypeRequiredNotes
iduuidyesFilled from the path {id} · Rating · id of csat_survey_response
notesstringReview notes · max 5000

Preconditions

The action is refused with 409 and one of these codes:

CodeRefused when
csat_not_foundThe 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"}'
Response
{
  "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"
tools/call
{
  "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

POSThttps://app.getera.io/api/v1/messages/actions/run_macro
API key or OAuth token · inbox:write
  • Canonical address: POST /api/v1/actions/inbox.run_macro

Input

FieldTypeRequiredNotes
iduuidyesMacro · id of macro
conversationIdsobject / arrayyesConversations

Preconditions

The action is refused with 409 and one of these codes:

CodeRefused when
macro_not_foundThe macro must exist and be visible to you.
invalid_selectionBetween one and a hundred conversations are chosen.
invalid_macro_actionsA 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":{}}'
Response
{
  "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":{}}'
tools/call
{
  "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

POSThttps://app.getera.io/api/v1/agent_bots/{id}/actions/save_agent_bot
API key or OAuth token · inbox:manage
  • REST alias: PATCH /api/v1/agent_bots/{id}
  • Canonical address: POST /api/v1/actions/inbox.save_agent_bot

Input

FieldTypeRequiredNotes
iduuidFilled from the path {id} · Bot · id of agent_bot
namestringyesName · max 200
descriptionstringDescription · max 2000
outgoingUrlstringWhere we send its events · max 2048

Preconditions

The action is refused with 409 and one of these codes:

CodeRefused when
agent_bot_not_foundA bot being changed must exist.
bot_name_takenBot names are unique in a workspace.
invalid_outgoing_urlThe 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"}'
Response
{
  "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"
tools/call
{
  "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

POSThttps://app.getera.io/api/v1/assignment_policies/{id}/actions/save_assignment_policy
API key or OAuth token · inbox:manage
  • REST alias: PATCH /api/v1/assignment_policies/{id}
  • Canonical address: POST /api/v1/actions/inbox.save_assignment_policy

Input

FieldTypeRequiredNotes
iduuidFilled from the path {id} · Rule · id of assignment_policy
namestringyesName · max 200
descriptionstringDescription · max 2000
assignmentOrderstring (enum)Order · Allowed values: round_robin, balanced
conversationPrioritystring (enum)Queue order · Allowed values: earliest_created, longest_waiting
fairDistributionLimitnumberMost per agent in the window · min 1
fairDistributionWindownumberWindow (seconds) · min 1
enabledbooleanEnabled

Preconditions

The action is refused with 409 and one of these codes:

CodeRefused when
invalid_policy_nameA rule needs a name.
policy_name_takenRule names are unique within a workspace.
policy_not_foundThe 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"}'
Response
{
  "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"
tools/call
{
  "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

POSThttps://app.getera.io/api/v1/canned_responses/{id}/actions/save_canned_response
API key or OAuth token · inbox:manage
  • REST alias: PATCH /api/v1/canned_responses/{id}
  • Canonical address: POST /api/v1/actions/inbox.save_canned_response

Input

FieldTypeRequiredNotes
iduuidFilled from the path {id} · Saved reply · id of canned_response
shortCodestringyesShortcut · One word, typed after a slash in the composer. · max 60
contentstringyesReply · max 150000

Preconditions

The action is refused with 409 and one of these codes:

CodeRefused when
invalid_short_codeA shortcut is one word with no spaces.
empty_canned_responseA saved reply needs something to say.
short_code_takenShortcuts are unique within a workspace.
canned_response_not_foundThe 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"}'
Response
{
  "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"
tools/call
{
  "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

POSThttps://app.getera.io/api/v1/macros/{id}/actions/save_macro
API key or OAuth token · inbox:write
  • REST alias: PATCH /api/v1/macros/{id}
  • Canonical address: POST /api/v1/actions/inbox.save_macro

Input

FieldTypeRequiredNotes
iduuidFilled from the path {id} · Macro · id of macro
namestringyesName · max 200
visibilitystring (enum)Who can use it · Allowed values: personal, global
actionsobject / arrayyesSteps

Preconditions

The action is refused with 409 and one of these codes:

CodeRefused when
invalid_macro_nameA macro needs a name.
invalid_macro_actionsEvery step is one this inbox can run, with its value.
global_macro_needs_manageSharing a macro with everyone needs manage.
macro_not_foundThe macro being edited must exist and be visible to you.
not_macro_ownerOnly 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":{}}'
Response
{
  "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":{}}'
tools/call
{
  "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

POSThttps://app.getera.io/api/v1/canned_responses/actions/save_reply_from_message
API key or OAuth token · inbox:manage
  • Canonical address: POST /api/v1/actions/inbox.save_reply_from_message

Input

FieldTypeRequiredNotes
iduuidyesMessage · id of message
shortCodestringyesShortcut · max 60

Preconditions

The action is refused with 409 and one of these codes:

CodeRefused when
message_not_foundThe message must still exist.
invalid_short_codeA shortcut is one word with no spaces.
empty_canned_responseThe message needs text to become a saved reply.
short_code_takenShortcuts 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"}'
Response
{
  "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"
tools/call
{
  "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

POSThttps://app.getera.io/api/v1/sla_policies/{id}/actions/save_sla_policy
API key or OAuth token · inbox:manage
  • REST alias: PATCH /api/v1/sla_policies/{id}
  • Canonical address: POST /api/v1/actions/inbox.save_sla_policy

Input

FieldTypeRequiredNotes
iduuidFilled from the path {id} · Policy · id of sla_policy
namestringyesName · max 200
descriptionstringDescription · max 2000
firstResponseSecondsnumberFirst response within (seconds) · min 0
nextResponseSecondsnumberNext response within (seconds) · min 0
resolutionSecondsnumberResolution within (seconds) · min 0
onlyDuringBusinessHoursbooleanCount business hours only

Preconditions

The action is refused with 409 and one of these codes:

CodeRefused when
sla_threshold_requiredAt least one of the three targets is set.
sla_name_takenPolicy names are unique in a workspace.
sla_policy_not_foundA 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"}'
Response
{
  "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"
tools/call
{
  "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

POSThttps://app.getera.io/api/v1/teams/{id}/actions/save_team
API key or OAuth token · inbox:manage
  • REST alias: PATCH /api/v1/teams/{id}
  • Canonical address: POST /api/v1/actions/inbox.save_team

Input

FieldTypeRequiredNotes
iduuidFilled from the path {id} · Team · id of team
namestringyesName · max 200
descriptionstringDescription · max 2000
allowAutoAssignbooleanAssign 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:

CodeRefused when
invalid_team_nameA team needs a name.
team_name_takenTeam names are unique within a workspace.
team_not_foundThe 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"}'
Response
{
  "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"
tools/call
{
  "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)

POSThttps://app.getera.io/api/v1/actions/inbox.send_transcript
API key or OAuth token · inbox:write

Input

FieldTypeRequiredNotes
iduuidyesConversation · id of conversation
emailemailSend to

Preconditions

The action is refused with 409 and one of these codes:

CodeRefused when
conversation_not_foundThe conversation must exist.
no_email_addressThere must be an address — given, or the person’s own.
transcript_emptyThere 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"}'
Response
{
  "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"
tools/call
{
  "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

POSThttps://app.getera.io/api/v1/inboxes/{id}/actions/set_auto_resolve
API key or OAuth token · inbox:manage
  • Canonical address: POST /api/v1/actions/inbox.set_auto_resolve

Input

FieldTypeRequiredNotes
inboxIduuidyesFilled from the path {id} · Inbox · id of inbox
hoursnumberHours of inactivity · min 1 · max 8760
messagestringActivity note · max 2000

Preconditions

The action is refused with 409 and one of these codes:

CodeRefused when
inbox_not_foundThe 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}'
Response
{
  "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
tools/call
{
  "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

POSThttps://app.getera.io/api/v1/agent_bot_inboxes/actions/set_inbox_bot
API key or OAuth token · inbox:manage
  • Canonical address: POST /api/v1/actions/inbox.set_inbox_bot

Input

FieldTypeRequiredNotes
inboxIduuidyesInbox · id of inbox
agentBotIduuidBot · id of agent_bot

Preconditions

The action is refused with 409 and one of these codes:

CodeRefused when
inbox_not_foundThe inbox must exist.
agent_bot_not_foundThe 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"}'
Response
{
  "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"
tools/call
{
  "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

POSThttps://app.getera.io/api/v1/inbox_members/actions/set_inbox_members
API key or OAuth token · inbox:manage
  • Canonical address: POST /api/v1/actions/inbox.set_inbox_members

Input

FieldTypeRequiredNotes
iduuidyesInbox · id of inbox
userIdsobject / arrayyesPeople

Preconditions

The action is refused with 409 and one of these codes:

CodeRefused when
inbox_not_foundThe inbox must exist.
not_a_memberEveryone 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":{}}'
Response
{
  "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":{}}'
tools/call
{
  "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

POSThttps://app.getera.io/api/v1/inboxes/{id}/actions/set_inbox_policy
API key or OAuth token · inbox:manage
  • Canonical address: POST /api/v1/actions/inbox.set_inbox_policy

Input

FieldTypeRequiredNotes
inboxIduuidyesFilled from the path {id} · Inbox · id of inbox
policyIduuidRule · id of assignment_policy

Preconditions

The action is refused with 409 and one of these codes:

CodeRefused when
inbox_not_foundThe inbox must exist.
policy_not_foundThe 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"}'
Response
{
  "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"
tools/call
{
  "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

POSThttps://app.getera.io/api/v1/inboxes/{id}/actions/set_mailbox
API key or OAuth token · inbox:manage
  • Canonical address: POST /api/v1/actions/inbox.set_mailbox

Input

FieldTypeRequiredNotes
iduuidyesFilled from the path {id} · Inbox · id of inbox
credentialIduuidyesAccount

Preconditions

The action is refused with 409 and one of these codes:

CodeRefused when
inbox_not_foundThe inbox must exist.
not_an_email_inboxOnly an email inbox has a mailbox to sign in.
mailbox_connection_requiredThe account to sign in with is named.
connection_not_foundThe account is still connected in this workspace.
mailbox_not_mail_appThe account is a Gmail or Outlook connection.
connection_not_usableThe account is this person’s, the company’s, or shared with them.
connection_inactiveThe 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"}'
Response
{
  "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"
tools/call
{
  "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

POSThttps://app.getera.io/api/v1/inbox_member_preferences/actions/set_member_preference
API key or OAuth token · inbox:read
  • Canonical address: POST /api/v1/actions/inbox.set_member_preference

Input

FieldTypeRequiredNotes
availabilitystring (enum)Availability · Allowed values: online, busy, offline
messageSignaturestringMessage signature · max 2000
audioAlertsbooleanPlay 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"}'
Response
{
  "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"
tools/call
{
  "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

POSThttps://app.getera.io/api/v1/conversations/{id}/actions/set_priority
API key or OAuth token · inbox:write
  • Canonical address: POST /api/v1/actions/inbox.set_priority

Input

FieldTypeRequiredNotes
iduuidyesFilled from the path {id} · Conversation · id of conversation
prioritystring (enum)Priority · Allowed values: low, medium, high, urgent

Preconditions

The action is refused with 409 and one of these codes:

CodeRefused when
conversation_not_foundThe 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"}'
Response
{
  "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"
tools/call
{
  "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

POSThttps://app.getera.io/api/v1/team_members/actions/set_team_members
API key or OAuth token · inbox:manage
  • Canonical address: POST /api/v1/actions/inbox.set_team_members

Input

FieldTypeRequiredNotes
iduuidyesTeam · id of team
userIdsobject / arrayyesPeople

Preconditions

The action is refused with 409 and one of these codes:

CodeRefused when
team_not_foundThe team must exist.
not_a_memberEveryone 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":{}}'
Response
{
  "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":{}}'
tools/call
{
  "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

POSThttps://app.getera.io/api/v1/inboxes/{id}/actions/set_working_hours
API key or OAuth token · inbox:manage
  • Canonical address: POST /api/v1/actions/inbox.set_working_hours

Input

FieldTypeRequiredNotes
inboxIduuidyesFilled from the path {id} · Inbox · id of inbox
daysobject / arrayyesWeek
workingHoursEnabledbooleanUse business hours
outOfOfficeMessagestringOut-of-office message · max 10000
timezonestringTimezone · max 100

Preconditions

The action is refused with 409 and one of these codes:

CodeRefused when
inbox_not_foundThe inbox must exist.
invalid_working_hoursEach day is closed, open all day, or open from one time to a later one.
unknown_timezoneThe 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":{}}'
Response
{
  "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":{}}'
tools/call
{
  "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

POSThttps://app.getera.io/api/v1/conversations/{id}/actions/snooze
API key or OAuth token · inbox:write
  • Canonical address: POST /api/v1/actions/inbox.snooze

Input

FieldTypeRequiredNotes
iduuidyesFilled from the path {id} · Conversation · id of conversation
untildate-timeyesUntil

Preconditions

The action is refused with 409 and one of these codes:

CodeRefused when
conversation_not_foundThe conversation must exist.
snooze_not_in_futureThe 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"}'
Response
{
  "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"
tools/call
{
  "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)

POSThttps://app.getera.io/api/v1/csat_survey_responses/actions/submit_csat
API key or OAuth token · inbox:handle
  • Canonical address: POST /api/v1/actions/inbox.submit_csat

Input

FieldTypeRequiredNotes
conversationIduuidyesConversation · id of conversation
ratingnumberyesRating · min 1 · max 5
feedbackMessagestringComment · max 5000

Preconditions

The action is refused with 409 and one of these codes:

CodeRefused when
conversation_not_foundThe conversation must exist.
no_csat_surveyA rating needs a survey that asked for it.
csat_lockedA 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}'
Response
{
  "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
tools/call
{
  "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

POSThttps://app.getera.io/api/v1/whatsapp_message_templates/actions/sync_whatsapp_templates
API key or OAuth token · inbox:manage
  • Canonical address: POST /api/v1/actions/inbox.sync_whatsapp_templates

Input

FieldTypeRequiredNotes
inboxIduuidyesWhatsApp inbox · id of inbox

Preconditions

The action is refused with 409 and one of these codes:

CodeRefused when
inbox_not_foundThe inbox must exist.
whatsapp_not_connectedThe 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"}'
Response
{
  "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"
tools/call
{
  "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

POSThttps://app.getera.io/api/v1/messages/{id}/actions/translate_message
API key or OAuth token · inbox:write
  • Canonical address: POST /api/v1/actions/inbox.translate_message

Input

FieldTypeRequiredNotes
iduuidyesFilled from the path {id} · Message · id of message
languagestringyesLanguage · max 80

Preconditions

The action is refused with 409 and one of these codes:

CodeRefused when
message_not_foundThe 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"}'
Response
{
  "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"
tools/call
{
  "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

POSThttps://app.getera.io/api/v1/conversations/{id}/actions/unmute
API key or OAuth token · inbox:write
  • Canonical address: POST /api/v1/actions/inbox.unmute

Input

FieldTypeRequiredNotes
iduuidyesFilled from the path {id} · Conversation · id of conversation

Preconditions

The action is refused with 409 and one of these codes:

CodeRefused when
conversation_not_foundThe conversation must exist.
not_mutedIt 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 '{}'
Response
{
  "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
tools/call
{
  "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

POSThttps://app.getera.io/api/v1/inboxes/{id}/actions/update_inbox
API key or OAuth token · inbox:manage
  • REST alias: PATCH /api/v1/inboxes/{id}
  • Canonical address: POST /api/v1/actions/inbox.update_inbox

Input

FieldTypeRequiredNotes
iduuidyesFilled from the path {id} · Inbox · id of inbox
namestringName · max 200
greetingEnabledbooleanSend a greeting
greetingMessagestringGreeting · max 2000
outOfOfficeMessagestringOut of office message · max 2000
workingHoursEnabledbooleanOnly answer in working hours
timezonestringTimezone · max 64
enableAutoAssignmentbooleanAssign automatically
enableEmailCollectbooleanAsk for an email address
csatSurveyEnabledbooleanAsk for a rating when resolved
allowMessagesAfterResolvedbooleanAllow replies after resolving
lockToSingleConversationbooleanOne conversation per person
senderNameTypestring (enum)Sender name · Allowed values: friendly, professional
businessNamestringBusiness name · max 200
helpSiteIduuidHelp centre · id of site
channelConfigobject / arrayChannel settings
csatConfigobject / arraySurvey settings

Preconditions

The action is refused with 409 and one of these codes:

CodeRefused when
inbox_not_foundThe inbox must exist.
invalid_csat_configThe survey’s display and label rule must be ones the source knows.
unknown_timezoneThe 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"}'
Response
{
  "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"
tools/call
{
  "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_inboxes
  • GET /api/v1/agent_bot_inboxes/{id}
ColumnTypeNullableAllowed valuesReferences
idstring (uuid) · uuid
organization_idstring (uuid) · uuidorganization
workspace_idstring (uuid) · uuidworkspace
inbox_idstring (uuid) · uuidinbox
agent_bot_idstring (uuid) · uuidagent_bot
statusstring · textactive, inactive
created_atstring (date-time) · timestamptz
updated_atstring (date-time) · timestamptz

Primary key: id

AgentBot

Table agent_bot · Resource agent_bots

ColumnTypeNullableAllowed valuesReferences
idstring (uuid) · uuid
organization_idstring (uuid) · uuidorganization
workspace_idstring (uuid) · uuidworkspace
namestring · text
descriptionstring · textyes
bot_typestring · text
outgoing_urlstring · textyes
bot_configobject · jsonb
created_atstring (date-time) · timestamptz
updated_atstring (date-time) · timestamptz
last_delivery_atstring (date-time) · timestamptzyes
last_delivery_errorstring · textyes

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_slas
  • GET /api/v1/applied_slas/{id}
ColumnTypeNullableAllowed valuesReferences
idstring (uuid) · uuid
organization_idstring (uuid) · uuidorganization
workspace_idstring (uuid) · uuidworkspace
conversation_idstring (uuid) · uuidconversation
sla_policy_idstring (uuid) · uuidsla_policy
sla_statusstring · textactive, hit, missed, active_with_misses
evaluated_atstring (date-time) · timestamptzyes
next_deadline_atstring (date-time) · timestamptzyes
next_deadline_typestring · textyes
created_atstring (date-time) · timestamptz
updated_atstring (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_policies
  • GET /api/v1/assignment_policies/{id}
  • PATCH /api/v1/assignment_policies/{id} → inbox.save_assignment_policy
ColumnTypeNullableAllowed valuesReferences
idstring (uuid) · uuid
organization_idstring (uuid) · uuidorganization
workspace_idstring (uuid) · uuidworkspace
namestring · text
descriptionstring · textyes
assignment_orderstring · textround_robin, balanced
conversation_prioritystring · textearliest_created, longest_waiting
fair_distribution_limitinteger · int4
fair_distribution_windowinteger · int4
enabledboolean · bool
created_atstring (date-time) · timestamptz
updated_atstring (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

ColumnTypeNullableAllowed valuesReferences
idstring (uuid) · uuid
organization_idstring (uuid) · uuidorganization
workspace_idstring (uuid) · uuidworkspace
short_codestring · text
contentstring · text
created_atstring (date-time) · timestamptz
updated_atstring (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_mentions
  • GET /api/v1/conversation_mentions/{id}
ColumnTypeNullableAllowed valuesReferences
idstring (uuid) · uuid
organization_idstring (uuid) · uuidorganization
workspace_idstring (uuid) · uuidworkspace
conversation_idstring (uuid) · uuidconversation
user_idstring (uuid) · uuidapp_user
message_idstring (uuid) · uuidyesmessage
mentioned_bystring (uuid) · uuidyesapp_user
mentioned_atstring (date-time) · timestamptz
created_atstring (date-time) · timestamptz
updated_atstring (date-time) · timestamptz

Primary key: id

ConversationParticipant

Table conversation_participant · Resource conversation_participants

  • GET /api/v1/conversation_participants
  • GET /api/v1/conversation_participants/{id}
ColumnTypeNullableAllowed valuesReferences
idstring (uuid) · uuid
organization_idstring (uuid) · uuidorganization
workspace_idstring (uuid) · uuidworkspace
conversation_idstring (uuid) · uuidconversation
user_idstring (uuid) · uuidapp_user
created_atstring (date-time) · timestamptz

Primary key: id

Conversation

Table conversation · Resource conversations

ColumnTypeNullableAllowed valuesReferences
idstring (uuid) · uuid
organization_idstring (uuid) · uuidorganization
workspace_idstring (uuid) · uuidworkspace
inbox_idstring (uuid) · uuidinbox
customer_idstring (uuid) · uuidcustomer
customer_identity_idstring (uuid) · uuidyescustomer_identity
statusstring · textopen, resolved, pending, snoozed
prioritystring · textyes
assignee_user_idstring (uuid) · uuidyesapp_user
assignee_agent_bot_idstring (uuid) · uuidyesagent_bot
assignee_set_atstring (date-time) · timestamptzyes
team_idstring (uuid) · uuidyesteam
campaign_idstring (uuid) · uuidyes
display_nostring (int64) · int8
external_identifierstring · textyes
additional_attributesobject · jsonb
custom_attributesobject · jsonb
contact_last_seen_atstring (date-time) · timestamptzyes
agent_last_seen_atstring (date-time) · timestamptzyes
assignee_last_seen_atstring (date-time) · timestamptzyes
first_reply_created_atstring (date-time) · timestamptzyes
waiting_sincestring (date-time) · timestamptzyes
snoozed_untilstring (date-time) · timestamptzyes
last_activity_atstring (date-time) · timestamptz
created_atstring (date-time) · timestamptz
updated_atstring (date-time) · timestamptz
muted_atstring (date-time) · timestamptzyes
era_modestring · textyes
era_assigned_bystring (uuid) · uuidyesapp_user
era_assigned_atstring (date-time) · timestamptzyes

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_responses
  • GET /api/v1/csat_survey_responses/{id}
ColumnTypeNullableAllowed valuesReferences
idstring (uuid) · uuid
organization_idstring (uuid) · uuidorganization
workspace_idstring (uuid) · uuidworkspace
conversation_idstring (uuid) · uuidconversation
message_idstring (uuid) · uuidmessage
customer_idstring (uuid) · uuidyescustomer
assigned_agent_user_idstring (uuid) · uuidyesapp_user
ratinginteger · int4
feedback_messagestring · textyes
csat_review_notesstring · textyes
review_notes_updated_atstring (date-time) · timestamptzyes
review_notes_updated_bystring (uuid) · uuidyesapp_user
created_atstring (date-time) · timestamptz
updated_atstring (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_preferences
  • GET /api/v1/inbox_member_preferences/{id}
ColumnTypeNullableAllowed valuesReferences
idstring (uuid) · uuid
organization_idstring (uuid) · uuidorganization
workspace_idstring (uuid) · uuidworkspace
user_idstring (uuid) · uuidapp_user
availability_statusstring · textonline, busy, offline
message_signaturestring · textyes
audio_alertsboolean · bool
updated_atstring (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_members
  • GET /api/v1/inbox_members/{id}
ColumnTypeNullableAllowed valuesReferences
idstring (uuid) · uuid
organization_idstring (uuid) · uuidorganization
workspace_idstring (uuid) · uuidworkspace
inbox_idstring (uuid) · uuidinbox
user_idstring (uuid) · uuidapp_user
last_assigned_atstring (date-time) · timestamptzyes
created_atstring (date-time) · timestamptz

Primary key: id

InboxWorkingHour

Table inbox_working_hour · Resource inbox_working_hours

  • GET /api/v1/inbox_working_hours
  • GET /api/v1/inbox_working_hours/{id}
ColumnTypeNullableAllowed valuesReferences
idstring (uuid) · uuid
organization_idstring (uuid) · uuidorganization
workspace_idstring (uuid) · uuidworkspace
inbox_idstring (uuid) · uuidinbox
weekdayinteger · int2
closed_all_dayboolean · bool
open_all_dayboolean · bool
opens_atstring (time) · timeyes
closes_atstring (time) · timeyes
created_atstring (date-time) · timestamptz
updated_atstring (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

ColumnTypeNullableAllowed valuesReferences
idstring (uuid) · uuid
organization_idstring (uuid) · uuidorganization
workspace_idstring (uuid) · uuidworkspace
namestring · text
channel_typestring · textweb_widget, api, email, whatsapp, sms, twilio_sms, facebook_page, instagram, telegram, line, tiktok, twitter, voice
identifierstring · textyes
channel_configobject · jsonb
needs_reauthboolean · bool
is_activeboolean · bool
enable_auto_assignmentboolean · bool
assignment_policy_idstring (uuid) · uuidyesassignment_policy
greeting_enabledboolean · bool
greeting_messagestring · textyes
email_addressstring · textyes
working_hours_enabledboolean · bool
out_of_office_messagestring · textyes
timezonestring · text
enable_email_collectboolean · bool
csat_survey_enabledboolean · bool
csat_configobject · jsonb
allow_messages_after_resolvedboolean · bool
lock_to_single_conversationboolean · bool
sender_name_typestring · textfriendly, professional
business_namestring · textyes
help_site_idstring (uuid) · uuidyessite
created_atstring (date-time) · timestamptz
updated_atstring (date-time) · timestamptz
health_statusstring · textunknown, healthy, degraded, reauth_required
health_checked_atstring (date-time) · timestamptzyes
health_errorstring · textyes
poll_stateobject · jsonb
auto_resolve_after_hoursinteger · int4yes
auto_resolve_messagestring · textyes
health_detailsobject · 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

ColumnTypeNullableAllowed valuesReferences
idstring (uuid) · uuid
organization_idstring (uuid) · uuidorganization
workspace_idstring (uuid) · uuidworkspace
namestring · text
visibilitystring · textpersonal, global
actionsobject · jsonb
created_bystring (uuid) · uuidyesapp_user
updated_bystring (uuid) · uuidyesapp_user
created_atstring (date-time) · timestamptz
updated_atstring (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_attachments
  • GET /api/v1/message_attachments/{id}
ColumnTypeNullableAllowed valuesReferences
idstring (uuid) · uuid
organization_idstring (uuid) · uuidorganization
workspace_idstring (uuid) · uuidworkspace
message_idstring (uuid) · uuidmessage
conversation_idstring (uuid) · uuidconversation
kindstring · textimage, audio, video, file, location, fallback, share, story_mention, contact, ig_reel, ig_post, ig_story, embed
file_idstring (uuid) · uuidyesfile
external_urlstring · textyes
coordinates_latnumber · float8yes
coordinates_longnumber · float8yes
fallback_titlestring · textyes
metaobject · jsonb
created_atstring (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/messages
  • GET /api/v1/messages/{id}
  • DELETE /api/v1/messages/{id} → inbox.delete_message
ColumnTypeNullableAllowed valuesReferences
idstring (uuid) · uuid
organization_idstring (uuid) · uuidorganization
workspace_idstring (uuid) · uuidworkspace
conversation_idstring (uuid) · uuidconversation
inbox_idstring (uuid) · uuidinbox
message_typestring · textincoming, outgoing, activity, template
content_typestring · texttext, input_text, input_textarea, input_email, input_select, cards, form, article, incoming_email, input_csat, integrations, sticker, voice_call
statusstring · textqueued, sent, delivered, read, failed
contentstring · textyes
processed_message_contentstring · textyes
is_privateboolean · bool
sender_kindstring · textuser, customer, agent_bot, system
sender_user_idstring (uuid) · uuidyesapp_user
sender_customer_idstring (uuid) · uuidyescustomer
sender_agent_bot_idstring (uuid) · uuidyesagent_bot
source_idstring · textyes
content_attributesobject · jsonb
external_source_idsobject · jsonb
additional_attributesobject · jsonb
sentimentobject · jsonb
created_atstring (date-time) · timestamptz
updated_atstring (date-time) · timestamptz
deleted_atstring (date-time) · timestamptzyes
delivery_attemptsinteger · int4
delivery_generationinteger · int4
delivery_errorstring · textyes
status_changed_atstring (date-time) · timestamptzyes

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_events
  • GET /api/v1/reporting_events/{id}
ColumnTypeNullableAllowed valuesReferences
idstring (uuid) · uuid
organization_idstring (uuid) · uuidorganization
workspace_idstring (uuid) · uuidworkspace
namestring · textconversation_opened, first_response, reply_time, conversation_resolved, conversation_bot_resolved, conversation_bot_handoff
valueinteger · int4
value_in_business_hoursinteger · int4yes
inbox_idstring (uuid) · uuidinbox
conversation_idstring (uuid) · uuidconversation
user_idstring (uuid) · uuidyesapp_user
team_idstring (uuid) · uuidyesteam
event_start_timestring (date-time) · timestamptz
event_end_timestring (date-time) · timestamptz
created_atstring (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_events
  • GET /api/v1/sla_events/{id}
ColumnTypeNullableAllowed valuesReferences
idstring (uuid) · uuid
organization_idstring (uuid) · uuidorganization
workspace_idstring (uuid) · uuidworkspace
applied_sla_idstring (uuid) · uuidapplied_sla
conversation_idstring (uuid) · uuidconversation
inbox_idstring (uuid) · uuidinbox
sla_policy_idstring (uuid) · uuidsla_policy
event_typestring · textfrt, nrt, rt
metaobject · jsonb
created_atstring (date-time) · timestamptz
updated_atstring (date-time) · timestamptz

Primary key: id

Checks:

  • CHECK ((jsonb_typeof(meta) = 'object'::text))

SlaPolicy

Table sla_policy · Resource sla_policies

ColumnTypeNullableAllowed valuesReferences
idstring (uuid) · uuid
organization_idstring (uuid) · uuidorganization
workspace_idstring (uuid) · uuidworkspace
namestring · text
descriptionstring · textyes
first_response_secondsinteger · int4yes
next_response_secondsinteger · int4yes
resolution_secondsinteger · int4yes
only_during_business_hoursboolean · bool
created_atstring (date-time) · timestamptz
updated_atstring (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_members
  • GET /api/v1/team_members/{id}
ColumnTypeNullableAllowed valuesReferences
idstring (uuid) · uuid
organization_idstring (uuid) · uuidorganization
workspace_idstring (uuid) · uuidworkspace
team_idstring (uuid) · uuidteam
user_idstring (uuid) · uuidapp_user
created_atstring (date-time) · timestamptz

Primary key: id

Team

Table team · Resource teams

ColumnTypeNullableAllowed valuesReferences
idstring (uuid) · uuid
organization_idstring (uuid) · uuidorganization
workspace_idstring (uuid) · uuidworkspace
namestring · text
descriptionstring · textyes
allow_auto_assignboolean · bool
created_atstring (date-time) · timestamptz
updated_atstring (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_templates
  • GET /api/v1/whatsapp_message_templates/{id}
ColumnTypeNullableAllowed valuesReferences
idstring (uuid) · uuid
organization_idstring (uuid) · uuidorganization
workspace_idstring (uuid) · uuidworkspace
inbox_idstring (uuid) · uuidinbox
provider_idstring · text
namestring · text
languagestring · text
categorystring · textyes
statusstring · textapproved, pending, rejected, paused, disabled
bodystring · text
componentsobject · jsonb
variable_countinteger · int4
synced_atstring (date-time) · timestamptz
created_atstring (date-time) · timestamptz
updated_atstring (date-time) · timestamptz

Primary key: id

Unique: inbox_id, provider_id

Checks:

  • CHECK ((variable_count >= 0))

Events

Workflows and webhooks react to these events.

EventWhat happensPayload
inbox.agent_bot_changedA bot was registered, changed, removed or put on an inbox.id
inbox.assignedA conversation changed hands.id, userId, automatic
inbox.assignment_policy_changedA routing rule changed.id
inbox.auto_resolve_changedAn inbox inactivity policy changed.id, hours
inbox.bot_handoffA bot handed the conversation back to people.id
inbox.canned_response_changedThe saved replies changed.id
inbox.channel_connectedA channel was connected.id, channelType
inbox.channel_deactivatedA channel was taken out of service.id
inbox.channel_health_changedA channel became reachable, unreachable, or needs reconnecting.id, status
inbox.channel_updatedA channel’s settings changed.id
inbox.conversation_openedA conversation was opened.id, inboxId, customerId, status
inbox.csat_receivedA customer rated a conversation.id, responseId, rating, customerId
inbox.customer_changedA conversation was moved to another person.id, customerId
inbox.delivery_retriedSomebody asked for a failed reply to be sent again.id, conversationId, generation
inbox.era_assignedA conversation was given to Era (to draft or to answer), or taken back.id, mode
inbox.macro_changedA macro changed.id
inbox.macro_executedA macro was run.id, conversationIds
inbox.member_preference_changedAn agent changed their Inbox presence or reply preferences.id, userId
inbox.members_changedWho works an inbox changed.id, count
inbox.mentionedSomebody was mentioned in a private note.id, messageId, userIds
inbox.message_deletedA message was deleted.id, conversationId
inbox.message_deliveredA channel accepted, delivered or showed a reply.id, conversationId, status
inbox.message_editedA sent message was corrected.id, conversationId
inbox.message_failedA reply could not be delivered.id, conversationId, reason
inbox.message_receivedA message arrived on a channel.id, conversationId, inboxId, customerId
inbox.message_sentA reply was written and queued for delivery.id, conversationId, firstReply, generation
inbox.message_translatedA message translation was stored beside its original.id, language
inbox.mutedA conversation was muted.id
inbox.note_addedA private note was left.id, conversationId
inbox.participant_addedSomebody started following a conversation.id, userId
inbox.priority_changedA conversation was ranked.id, priority
inbox.reopenedA conversation came back.id
inbox.resolvedA conversation was resolved.id, customerId, csatRequested
inbox.sla_appliedA conversation started being measured against an SLA.id, appliedSlaId, policyId
inbox.sla_closedA resolved conversation’s SLA settled as hit or missed.id, appliedSlaId, status
inbox.sla_missedA conversation missed an SLA target.id, appliedSlaId, type, policy
inbox.sla_policy_changedAn SLA policy changed.id
inbox.snoozedA conversation was put down until later.id, until
inbox.team_assignedA conversation moved team.id, teamId, assigneeDropped
inbox.team_changedA team changed.id
inbox.transcript_requestedA conversation’s transcript was asked for by email.id, email
inbox.unmutedA conversation was unmuted.id
inbox.user_mentionedOne person was mentioned in a private note — what a notification rule for them listens to.id, messageId, userId, recipientUserId
inbox.whatsapp_templates_syncedApproved WhatsApp templates were refreshed.id, count
inbox.working_hours_changedA channel’s business hours changed.id