RaabtaHQ
Answer a human approval

Answer a human approval

POST/api/v1/asks/{id}/answer

scope asks:answerIdempotentCosts 2 rate-limit units

Answers the question and un-parks the run. If somebody answered first this returns 200 with already_answered: true, NOT a conflict — what you wanted did happen, just not by your call, and a 409 would invite a retry that cannot succeed. min_role on an ask governs the dashboard; over the API the asks:answer scope is the gate, and the answer is attributed to the key rather than to a person.

Path parameters

NameTypeDescription
idrequired
string (uuid)Ask id

Body

application/json
NameTypeDescription
decisionrequired
string (enum)approve or reject an approval, choose an option, submit input, or edit a tool callapproverejectchoosesubmitedit
answeroptional
map<string, any>The payload for the decision — `option_id` for a choice, `text` for input
Request
curl -X POST "https://your-crm.example.com/api/v1/asks/6d7e8f9a-0b1c-4d2e-8f3a-4b5c6d7e8f9a/answer" \
  -H "Authorization: Bearer $RAABTA_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: 6f1c2a9e-3b4d-4c5e-8f7a-9b0c1d2e3f4a" \
  -d '{
    "decision": "approve"
  }'
Response · 200 OK
{
  "data": {
    "ask": {
      "id": "6d7e8f9a-0b1c-4d2e-8f3a-4b5c6d7e8f9a",
      "run_id": "3f9a1c2e-5b7d-4e8f-9a0b-1c2d3e4f5a6b",
      "assistant_id": "8f2c1d3e-4a5b-4c6d-9e0f-1a2b3c4d5e6f",
      "kind": "approval",
      "status": "answered",
      "question": "Offer Ayesha a free reschedule outside the 24-hour window?",
      "context": "She cancelled 3 hours before her slot and has been a customer for two years.",
      "options": [],
      "tool_id": null,
      "tool_input": null,
      "min_role": "admin",
      "expires_at": "2026-09-03T09:00:00.000Z",
      "on_timeout": "reject",
      "decision": "approve",
      "answer": null,
      "answered_by": null,
      "answered_at": null,
      "answer_source": "human",
      "created_at": "2026-09-02T09:01:40.000Z",
      "updated_at": "2026-09-02T09:01:40.000Z"
    },
    "resumed": true,
    "already_answered": false
  }
}

Response

Wrapped in { data: … }
NameTypeDescription
askrequired
objectThe ask as it now stands
ask.idrequired
string (uuid)Ask id
ask.run_idrequired
string (uuid)The run parked on this question
ask.assistant_idrequired
string (uuid) | nullThe assistant that asked
ask.kindrequired
string (enum)What kind of answer the run needsapprovalchoiceinputtool_call
ask.statusrequired
string (enum)Only a pending ask can be answeredpendingansweredexpiredcancelled
ask.questionrequired
stringWhat the assistant is asking
ask.contextrequired
string | nullBackground it supplied with the question
ask.optionsrequired
object[]For a choice: the options offered. Empty for every other kind
ask.options[].idrequired
stringPass this back as option_id
ask.options[].labelrequired
stringWhat to show a human
ask.options[].descriptionrequired
string | nullExtra detail, if the assistant gave any
ask.tool_idrequired
string | nullFor a tool_call: the tool the model proposed and has NOT run
ask.tool_inputrequired
map<string, any> | nullThe arguments it proposed
ask.min_rolerequired
stringThe dashboard role floor for answering. NOT enforced for API keys, where the asks:answer scope is the gate
ask.expires_atrequired
string (date-time)After this the on_timeout rule applies automatically
ask.on_timeoutrequired
string (enum)What happens if nobody answers in timerejectapprovehandofffail
ask.decisionrequired
string | nullThe decision recorded, once answered
ask.answerrequired
map<string, any> | nullThe answer recorded
ask.answered_byrequired
string (uuid) | nullThe member who answered. Null when an API key, a timeout or a withdrawal settled it
ask.answered_atrequired
string (date-time) | nullWhen it was answered
ask.answer_sourcerequired
string (enum) | nullHow it settledhumantimeoutcancel
ask.created_atrequired
string (date-time)When the assistant asked
ask.updated_atrequired
string (date-time)When it last changed
resumedrequired
booleanWhether this answer un-parked the run. False when the run had already ended
already_answeredrequired
booleanTrue when somebody answered first. Your answer was not recorded, and this is not an error

Errors

StatusCodeWhen
400bad_requestThe request could not be parsed: malformed JSON, an invalid cursor, or a query parameter of the wrong shape.
400validation_errorThe body or query failed validation. `details` lists each failing field with a `path` and a `message`.
401unauthorizedNo usable API key: the Authorization header is missing or malformed, or the key is unknown, revoked or expired. The three are deliberately indistinguishable.
403forbiddenThe key is valid but lacks the scope this endpoint requires, or the request came from an address outside the key’s IP allowlist. The message says which.
403account_suspendedThe account this key belongs to is suspended. Rotating the key will not help; contact support.
404not_foundNo such resource in this account. A resource that exists in another account also returns this.
409conflictThe request conflicts with current state: a duplicate phone number on create, an invalid state transition, or an idempotent request that is still in flight.
422unprocessableThe request was well-formed but cannot be carried out. `reason` is a stable string saying why (for example `outside_window` or `stage_not_in_pipeline`).
429rate_limitedThe per-key budget, or the per-IP budget for failed authentication, is exhausted. Honour `Retry-After` before retrying.
500internalSomething failed on our side. Safe to retry with the same Idempotency-Key; quote `request_id` if it persists.