docs
API

Actions and approvals

List, read, approve and discard the outbound messages nima is holding for approval.

An action is one outbound message nima has drafted to write into an integration: a comment on a Jira ticket, a Slack direct message, a question to a teammate. Every action is a row in nima's ledger with a state, the exact text that would be sent, and the people allowed to approve it. When the rung for that message is ask, the action waits in awaiting_approval until an eligible approver approves or discards it. The four operations below list, read, approve and discard actions. How nima decides to draft one, and what a rung is, lives in Outbound actions.

Availability

Off until a workspace owner or admin turns it on. Outbound dispatch is on for the platform, but each workspace must set dispatchEnabled to true with PATCH /organizations/settings. That route needs a signed-in owner or admin session and there is no admin-panel control for it, so ask the nima team if you cannot make the call. With it off, nima creates no new actions; actions created earlier stay listed. See Availability.

The actions:approve scope cannot be minted today

These four routes require the scope actions:approve. No API key created in Settings or with POST /v1/api-keys can carry it, and the OAuth consent screen does not offer it, so an API key or an MCP client cannot call them today. A signed-in owner, admin or member session can. Most approvals happen on the Slack approval card, which needs no key. See Scopes and permissions.

Actions and approvals API routes

MethodPathScopeMCP tool
GET/v1/dispatch/actionsactions:approvelist_pending_actions
GET/v1/dispatch/actions/{id}actions:approveget_action
POST/v1/actions/{id}/approveactions:approveapprove_action
POST/v1/actions/{id}/discardactions:approvediscard_action

Paths are relative to https://api.getnima.io. The two read routes are also served at the older paths /v1/actions and /v1/actions/{id}; both forms answer identically. Approve and discard exist only under /v1/actions/{id}/. Every route needs the workspace permission to read content, which every member has, and is limited to your own workspace.

List actions

GET/v1/dispatch/actions
scopeactions:approveMCP toollist_pending_actions

The actions you proposed or may approve, newest first. An owner or admin can list every action in the workspace with all=true. Drafts that nima writes itself, such as the approval cards, are never listed.

stateenum

One state. Omitted on the REST route, every state is returned. The MCP tool defaults it to awaiting_approval.

approvableByMestring

true narrows to actions where you are an eligible approver. false or omitted applies no such narrowing. The MCP tool defaults it to true.

allstring

true lists the whole workspace. It needs the integrations permission, held by owners and admins. A member who sends it gets a 403. Omitted or false, you see only actions you proposed or may approve. Combined with approvableByMe=true, the approver narrowing wins.

pageinteger

Starts at 1.

limitinteger

Rows per page, 1 to 100, default 20. This route names the page size limit, not pageSize; a value outside the range is a 400. Read meta.pageSize for what was used.

The response is a page: data is an array of action objects and meta carries the counts, as described in Pagination. Over an API key or OAuth token, questions nima asks a teammate on your behalf are left out of the list, because they cannot be approved there.

Errors

StatusWhen
400state, all or approvableByMe is not an accepted value, or page or limit is out of range.
403all=true from someone without the integrations permission (INSUFFICIENT_PERMISSIONS, details.requiredPermissions is ["MANAGE_INTEGRATIONS"]).

Get an action

GET/v1/dispatch/actions/{id}
scopeactions:approveMCP toolget_action

One action with its full preview, the exact text that would go out. Use it when an approval card says it could not show everything: the card then points here for the full text. A message that needs approval is never longer than 9,000 characters: nima shortens a longer draft to fit, with a visible marker and a receipt note, and refuses it if it still does not fit.

iduuidrequired

The action id, from an approval card or from the list. Anything that is not a UUID is a 400.

The response is one action object. You can read an action if you are an owner or admin, if you proposed it, or if you are one of its eligible approvers. For anyone else the route answers 404, the same as for an id that does not exist, so a foreign action id reveals nothing. Approval cards and other host-written drafts are never returned. Questions nima asks a teammate on your behalf are not returned over an API key or OAuth token.

Errors

StatusWhen
400id is not a UUID.
404No such action, an action you may not read, a host-written draft, or a brokered question over an API key or OAuth token. The message is Action not found.

Approve an action

POST/v1/actions/{id}/approve
scopeactions:approveMCP toolapprove_action

Approves one action that is waiting for you. nima then queues the send and the action moves to approved, then executing, then succeeded or another terminal state. The route takes no body. It returns 200 with the decision, not the send result; read the action afterwards to see whether the message went out.

iduuidrequired

The action id. An approval card's own id also works; the response's actionId is then the action the card is about.

Only a person the action was routed to may approve it, and only while it is awaiting_approval. Approvals lapse after 24 hours. Before it sends, nima checks that the stored text still matches what the card showed. If it does not, the action becomes refused with refusedReason guard_rejected and the receipt note body_hash_mismatch, and the call answers 409 with reason: "already_decided".

Approving is refused, not queued, when the approval window closed, someone decided first, the stored text changed, or outbound dispatch or the writer account nima posts with is off. Discarding stays possible while dispatch is off.

Response:

actionIduuid

The action id.

decisionstring

approved.

Errors

StatusdetailsWhen
400noneid is not a UUID.
403idYou are not an approver for the action, or you are no longer a member of the workspace (INSUFFICIENT_PERMISSIONS, You are not an approver for this action).
403idThe action is a question nima asked a teammate on your behalf, and you called over an API key or OAuth token: This action cannot be approved over MCP or an API key; use the Slack approval card. A signed-in session can approve it.
404idNo such action. The message is Action not found.
409reason: "expired"The approval window has closed. Nothing to retry.
409reason: "already_decided"This action was already decided. Read the action to see the outcome.
409reason: "dispatch_off", switchOutbound dispatch is off, or the writer account nima posts with is disconnected. The action keeps waiting. switch is flag_off, org_off or binding_disabled.

The 409 shapes are also listed in Errors.

Discard an action

POST/v1/actions/{id}/discard
scopeactions:approveMCP tooldiscard_action

Discards one action that is waiting for you. The message is never sent and the action becomes discarded. The route takes no body and returns 200 with the decision.

iduuidrequired

The action id. An approval card's own id also works; the response's actionId is then the action the card is about.

The same people may discard as may approve, and only while the action is awaiting_approval. Discarding works while outbound dispatch is off, so approvers can clear waiting actions during an outage. It answers the same 400, 403 and 404 as approve, and 409 with reason: "expired" or "already_decided". It never answers dispatch_off.

Response:

actionIduuid

The action id.

decisionstring

discarded.

The action object

The action object is what the list and get routes return for each action. It shows what would go where and who could read it, and never the native payload or the raw tool input.

iduuid

The action id. The first 8 characters appear as ref in the footer of the sent message.

toolNamestring

The effect tool, for example reply_on_ticket or send_dm.

adapterKeystring

The channel adapter that sends it, for example jira_comment or slack_dm.

proposerKindstring

What proposed the action: template, agent_run or ask_person.

destinationobject

Where it goes: kind (person, ticket_thread or channel), provider, and issueKey (a string for a ticket, otherwise null).

audiencestring

Who could read the message once sent: self, org_member, org_internal or external_possible, narrowest first.

rungstring

The rung that applied: never, ask or auto.

statestring

One of the action states.

previewstring | null

The exact text that would be sent, or null.

contextExcerptstring | null

The question or comment being answered, cleaned and clipped to 600 characters, with links reduced to their host. null when there is none.

receiptNotesstring[]

Notes about how the message was rendered, such as an unlinked mention shown as plain text or a shortened body.

eligibleApproverIdsstring[]

User ids allowed to approve or discard.

approverUserIdstring | null

The user who decided, once someone has. Otherwise null.

approvalExpiresAtstring | null

ISO 8601 UTC. When the approval lapses, 24 hours after it was requested. null when no approval was requested.

refusedReasonstring | null

Why the action was refused, when its state is refused. Otherwise null.

externalUrlstring | null

A link to the sent message, once it went out and the channel returns one.

createdAtstring

ISO 8601 UTC.

executedAtstring | null

ISO 8601 UTC. When the send finished, or null.

Action states

An action is in one of ten states, and state on the object says which. Six are terminal: succeeded, failed, uncertain, discarded, expired and refused.

StateMeaning
proposedCreated and not yet routed.
awaiting_approvalRung ask. Waiting up to 24 hours for an approver.
approvedApproved, or rung auto. Queued to send.
executingBeing sent.
succeededSent.
failedThe send failed for good: a permanent or auth error, or five attempts used.
uncertainA timeout left it unknown whether the message went out and nima found no sign of it. It is never retried, so you do not get a duplicate.
discardedAn approver discarded it.
expiredNobody decided within 24 hours.
refusedA check stopped it. refusedReason says which, for example flag_off, org_off, policy_never, guard_rejected, principal_revoked or stale.

An action that was approved while dispatch was on, but sent after it went off, ends refused with flag_off or org_off.

Approve outbound actions in Slack instead

Approving in Slack is the path that works without the actions:approve scope, and it needs Slack connected to the workspace. For a drafted reply or message, nima sends each eligible approver a direct message with the exact text, where it goes, who could read it and when it expires, and two buttons, Approve and Discard. Nothing goes out until someone presses Approve. A question nima asks a teammate on your behalf is approved differently: when you asked in Slack, the card appears in your own thread with Approve, Edit and Discard, and a direct message is sent only if posting in the thread fails. An API key or OAuth token cannot approve such a question. Without Slack connected no card is sent, and a signed-in session can still approve with the routes above. See Outbound actions and the outbound tools.