LoguDocs
Open Logu

Asks · decideAsk

Decide an ask (humans only)

POST /v1/asks/{id}/decision

A signed-in Decider or Owner decides: an option, a text (when the ask allows it), or both. The first decision wins. Agents can never decide asks.

Who may call it
humans: Deciders and up
Server
https://api.logu.dev

Parameters#

NameInTypeRequiredDescription
idpathstringrequiredThe ask's id (ask_…). · e.g. ask_01K7DQ3M2V8H4N6R9T1W3Y5B7C
Logu-WorkspaceheaderstringoptionalHumans only: the workspace to act in (default: your personal workspace). Agents always act in their API key's workspace. · e.g. wsp_01K7D7Y8Z0B2D4F6H8K0M2P4R6

Request body#

Content type application/json (required).

A human's decision: an option, a text, or both.

FieldTypeRequiredNotes
optionIdstringoptional1–64 chars · pattern ^[a-z0-9][a-z0-9._-]*$
textstringoptional1–5000 chars
{
  "optionId": "approve"
}

Example#

curl -s -X POST "https://api.logu.dev/v1/asks/ask_01K7DQ3M2V8H4N6R9T1W3Y5B7C/decision" \
  -H "Authorization: Bearer $LOGU_SESSION_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"optionId":"approve"}'

Responses#

200

The ask, decided, with decision.decidedBy.

{
  "id": "ask_01K7DQ3M2V8H4N6R9T1W3Y5B7C",
  "project": "shop",
  "channel": null,
  "title": "Deploy v1.4 to production?",
  "body": "3 commits since v1.3. CI is green.",
  "icon": null,
  "url": "https://github.com/acme/shop/compare/v1.3...v1.4",
  "options": [
    {
      "id": "approve",
      "label": "Approve",
      "style": "primary"
    },
    {
      "id": "decline",
      "label": "Decline",
      "style": "danger"
    }
  ],
  "allowText": false,
  "status": "decided",
  "decision": {
    "optionId": "approve",
    "text": null,
    "decidedVia": "app",
    "decidedAt": "2026-10-12T14:05:42.000Z",
    "decidedBy": {
      "id": "Xq3bY7tLmN2pR8sV",
      "name": "Ana"
    }
  },
  "defaultOption": "decline",
  "expiresAt": "2026-10-12T15:03:11.000Z",
  "createdAt": "2026-10-12T14:03:11.000Z",
  "metadata": null,
  "agent": {
    "id": "agt_01K7D8F2H4K6M8P0R2T4V6X8Z0",
    "name": "deploy-bot"
  },
  "attachments": [
    {
      "id": "att_01K7DQ6C8E0G2J4L6N8Q0S2U4W",
      "url": "https://api.logu.dev/a/att_01K7DQ6C8E0G2J4L6N8Q0S2U4W?exp=1791824400&sig=q8Hk2mP0vX3nR5tY7wZ9bD1fG3jL5nQ7sU9wY1aC3eE",
      "contentType": "image/png",
      "size": 184320,
      "width": 1440,
      "height": 900,
      "name": "checkout-after.png"
    }
  ],
  "test": false
}
Response fields
FieldTypeRequiredNotes
idstringrequired
projectstringrequired
channelstring | nullrequired
titlestringrequired
bodystring | nullrequired
iconstring | nullrequired
urlstring | nullrequired
optionsAskOption[]required
options[].idstringrequired1–64 chars · pattern ^[a-z0-9][a-z0-9._-]*$
options[].labelstringrequired1–64 chars
options[].style"primary" | "danger" | "default"required
allowTextbooleanrequired
status"waiting" | "decided" | "timed_out" | "withdrawn"required
decisionDecision | nullrequired
decision.optionIdstring | nullrequired
decision.textstring | nullrequired
decision.decidedVia"app" | "link" | "telegram" | "slack" | "whatsapp" | "rule" | "timeout"required
decision.decidedAtstringrequireddate-time
decision.decidedByMemberRef | nullrequired
decision.decidedBy.idstringrequired
decision.decidedBy.namestringrequired
decision.ruleIdstring | nulloptional
defaultOptionstring | nulloptionalApplied as the decision if nobody decides before expiresAt (then decision.decidedVia is timeout: never an approval); null: it ends timed_out. Agents can't set an approving one (id approve or style primary) on new asks.
expiresAtstringrequireddate-time
createdAtstringrequireddate-time
metadataobject | nullrequiredFree-form context stored with the item, up to 8,192 characters of JSON. thread (a short string, such as a Claude Code session id or a Codex thread id) is the one key Logu reads: events that share it are shown together as one thread in the app.
agentAgentRef | nullrequired
agent.idstringrequired
agent.namestringrequired
attachmentsAttachment[]requiredIts images, in order, each with a signed url that works for about an hour.
attachments[].idstringrequired
attachments[].urlstringrequiredA signed link to the image that works without credentials for about an hour (re-read the ask or event for a fresh one). Members can always fetch it with GET /v1/attachments/{id}.
attachments[].contentType"image/png" | "image/jpeg" | "image/webp" | "image/gif"required
attachments[].sizeintegerrequiredBytes.
attachments[].widthinteger | nullrequiredPixels, when Logu could read them from the image's header.
attachments[].heightinteger | nullrequiredPixels, when Logu could read them from the image's header.
attachments[].namestring | nullrequiredThe file name it was uploaded with, if any (the image's alt text in the app).
testbooleanrequiredSent with a test API key (lg_test_…): shown in the app and decided there like any other, but never delivered (no push, browser notification, email or Telegram). Its callback is sent.
escalationobject | nulloptional
escalation.atstringrequireddate-time
escalation.toMemberRef[]required
escalation.to[].idstringrequired
escalation.to[].namestringrequired

Errors#

Every error is {"error":{"code","message","hint","docUrl","requestId"}}: branch on code. See Errors.

StatusCodeMeaning
400bad_requestThe request is malformed or a field is invalid (see issues).
400unknown_fieldThe body has a field the endpoint doesn't know (often a typo or snake_case).
401unauthorizedNo credentials, or the API key or session is invalid or revoked.
403forbiddenThe caller is authenticated but may not do this (wrong role, or an API key on a human-only endpoint).
404not_foundNo such route, or the resource doesn't exist in your workspace.
409conflictThe resource isn't in a state that allows this: an ask that is no longer waiting (ask has its current state), an invitation no longer pending, the last Owner.
500internal_errorSomething went wrong on Logu's side.