{"openapi":"3.1.0","info":{"title":"Logu API","version":"1.0.0","description":"Logu is where software agents reach a human. **Agents ask. You decide.** An agent sends an **ask** when it needs a decision, an **event** when something happened and a **metric** when it reports a number; the human gets them by push, browser notifications, email, Telegram, Slack and WhatsApp, and decides from wherever they are. Agents never decide.\n\n**For agents:** read `x-agent-hint` on each operation. The core loop is `POST /v1/asks`, then `GET /v1/asks/{id}/wait?timeout=50` while `status` is `waiting`. Guide: https://api.logu.dev/llms.txt (full: https://api.logu.dev/llms-full.txt). MCP: https://api.logu.dev/mcp.\n\n**Auth.** Agents send an API key (`Authorization: Bearer lg_…`) and always act in its workspace. Humans send their session token and pick a workspace with `Logu-Workspace` (default: their personal one). `x-logu-access` on each operation says who may call it (`human`: the least role, `account` for any signed-in human; `agent`: whether an API key may).\n\n**Requests.** JSON, camelCase. Request bodies are strict: an unknown field is a 400 `unknown_field` with a \"did you mean\". Send `Idempotency-Key` on `POST /v1/asks`, `/v1/events` and `/v1/metrics` to retry safely (per agent, 24 hours).\n\n**Limits.** Per API key, per minute: 60 asks, 600 events and metrics, 120 images, 1200 requests in all (429 `rate_limited` with `Retry-After`). Humans aren't rate limited.\n\n**Retention.** Settled asks are deleted 180 days after they settled and events 180 days after they were sent, with their images: after that their ids are a 404. Asks still waiting are never deleted.\n\n**Errors.** `{\"error\":{\"code\",\"message\",\"hint\",\"docUrl\",\"requestId\",\"issues\"?,\"ask\"?}}`. Every response has `Logu-Request-Id`. Retry 429 and 5xx with backoff; don't retry other 4xx.\n\n| Code | Status | What to do |\n|---|---|---|\n| `bad_request` | 400 | Fix the fields listed in `issues` and send the request again. |\n| `approving_default` | 400 | Use defaultOption: \"decline\" (fail closed) or leave it out; ask an Owner to create a rule for pre-approval. |\n| `unknown_field` | 400 | Remove or rename the fields listed in `issues`. Field names are camelCase. |\n| `unauthorized` | 401 | Send `Authorization: Bearer lg_…` with an active API key (create one in Logu → Agents). |\n| `forbidden` | 403 | Agents send asks, events and metrics; only a human Decider or Owner decides asks. |\n| `workspace_suspended` | 403 | Sign in at https://logu.dev to see the workspace, and contact Logu support to ask for a review. |\n| `email_not_verified` | 403 | Confirm your email with the link Logu sends (Settings → Account, or the invitation page), then retry. |\n| `not_found` | 404 | Check the path and the id. API keys only see their own workspace. |\n| `method_not_allowed` | 405 | Use one of the methods in the `Allow` header. |\n| `conflict` | 409 | Read the current state instead of retrying. |\n| `idempotency_key_in_use` | 409 | Retry in a moment with the same key to get its result. |\n| `idempotency_key_reused` | 422 | Use a new key for a different request; reuse a key only to retry the exact same request. |\n| `rate_limited` | 429 | Wait for `Retry-After` seconds, then retry. API key limits are per key, per minute. |\n| `internal_error` | 500 | Retry with backoff. If it persists, report the `requestId`. |\n| `authorization_pending` | 400 | Keep polling every `interval` seconds until the human approves or denies it, or it expires. |\n| `slow_down` | 400 | Wait 5 seconds longer between polls from now on, then poll again. |\n| `expired_token` | 400 | Start again: request a new device code (`logu login`). |\n| `access_denied` | 403 | Stop polling. Start again only if the human wants to connect this agent. |\n| `plan_limit` | 402 | An Owner can upgrade the workspace to Pro in Logu → Settings → Plan (on Pro, ask the Logu team to raise the quota); monthly counts reset on the 1st (UTC). |\n| `ceiling_reached` | 402 | Nothing was created, and an ask that wasn't created was not asked: don't proceed as if a human had answered. Tell your human; monthly counts reset on the 1st (UTC), and an operator can raise a ceiling for a workspace. Don't retry before then. |\n| `overloaded` | 503 | Retry after `Retry-After` seconds, with jitter. Nothing was lost: asks, events, metrics and decisions don't depend on the hub. |\n| `service_unavailable` | 503 | Retry with backoff; for a feature that isn't configured, ask the server's operator. |","contact":{"name":"Logu","url":"https://logu.dev"},"license":{"name":"MIT","identifier":"MIT"}},"servers":[{"url":"https://api.logu.dev","description":"Production"}],"tags":[{"name":"Asks","description":"An ask is an agent's request for a human decision. Agents ask and wait; only a human Decider or Owner decides."},{"name":"Events","description":"An event is something an agent reports that needs no action. Delivered like an ask, without options."},{"name":"Attachments","description":"An attachment is an image an agent adds to an ask or event (up to 4), e.g. a UI change's before/after screenshots. Upload it, then list its id in `attachments`. Private to the workspace; shown with signed links that expire."},{"name":"Metrics","description":"A metric is a named number an agent reports over time; Insights shows trends and threshold alerts."},{"name":"Agents","description":"Agents and their API keys. An agent is anything that calls Logu from code; it belongs to a workspace."},{"name":"Workspaces","description":"A workspace owns everything agents send. Every human has a personal workspace and can join others."},{"name":"Members","description":"Members of a workspace and their roles: Owner, Decider, Viewer."},{"name":"Invitations","description":"An Owner invites an email address with a role; a one-time link valid 7 days."},{"name":"Webhooks","description":"Webhook endpoints get what agents send (asks created and settled, events, threshold alerts) as signed POSTs (Standard Webhooks), retried for about 28 hours, with a delivery log. Owners manage them. See https://logu.dev/docs/webhooks."},{"name":"Billing","description":"A workspace's plan: Free or Pro (US$ 29/month, founder prices for the first 1,000 workspaces). Owners upgrade with Stripe Checkout and manage the subscription in Stripe's Customer Portal. Over a plan quota (while limits are enforced) the API answers 402 `plan_limit`; over one of Logu's always-on cost ceilings, 402 `ceiling_reached`. See https://logu.dev/docs/billing."},{"name":"Rules","description":"A rule is an Owner's decision made in advance: asks from one agent in one project (with an exact title, when set) that offer its option are decided with it as they're sent, by that Owner (`decidedVia: \"rule\"`). Owners create and delete them in the app; API keys never can."},{"name":"Activity","description":"The activity log: who did what in a workspace."},{"name":"Delivery","description":"How Logu reaches each member: push, browser notifications, email, Telegram, Slack, WhatsApp, quiet hours and project mutes. Per member, across workspaces."},{"name":"Me","description":"Who is calling."},{"name":"Sign-in","description":"How humans get a `session` token (under `/api/auth`, not `/v1`): magic links, passkeys, email + password, and their sessions. Agents use API keys instead."},{"name":"Meta","description":"Discovery for agents and tools: this document, the reference page, llms.txt and MCP."}],"externalDocs":{"description":"Agent guide (llms.txt)","url":"https://api.logu.dev/llms.txt"},"components":{"securitySchemes":{"apiKey":{"type":"http","scheme":"bearer","bearerFormat":"lg_…","description":"An agent's API key: `Authorization: Bearer lg_…`. Create an agent in the Logu app (Agents) to get one; it is shown once. Acts in the agent's workspace."},"session":{"type":"http","scheme":"bearer","description":"A human's session token from signing in (Better Auth, returned in the `set-auth-token` header). Pick the workspace with `Logu-Workspace`."}},"headers":{"RequestId":{"description":"The request's id (Cloudflare's ray id when there is one). Quote it when reporting a problem.","schema":{"type":"string","example":"8c1f0b2a9d3e4f51-GRU"}},"RetryAfter":{"description":"Seconds to wait before retrying.","schema":{"type":"integer","example":60}},"RateLimitLimit":{"description":"Requests allowed per minute in the tightest limit that applies.","schema":{"type":"integer","example":60}},"RateLimitRemaining":{"description":"Requests left in the window (0 on a 429).","schema":{"type":"integer","example":0}},"RateLimitReset":{"description":"Seconds until the window resets.","schema":{"type":"integer","example":60}},"RateLimitPolicy":{"description":"The limit as `<limit>;w=<window seconds>`.","schema":{"type":"string","example":"60;w=60"}},"IdempotentReplayed":{"description":"`true` when this is the stored result of an earlier request with the same Idempotency-Key.","schema":{"type":"string","example":"true"}}},"examples":{"bad_request":{"summary":"bad_request (400)","description":"The request is malformed or a field is invalid (see `issues`).","value":{"error":{"code":"bad_request","message":"Invalid request: title: Too small: expected string to have >=1 characters","hint":"Fix the fields listed in `issues` and send the request again.","docUrl":"https://logu.dev/docs/errors#bad_request","requestId":"8c1f0b2a9d3e4f51-GRU","issues":[{"path":"title","message":"Too small: expected string to have >=1 characters"}]}}},"approving_default":{"summary":"approving_default (400)","description":"The ask's `defaultOption` is an approving option (the id `approve` or a `primary` style): an agent can't pre-approve by default.","value":{"error":{"code":"approving_default","message":"defaultOption \"approve\" is an approving option: a default is applied when nobody answers, so it can't be the go-ahead. Nothing was asked","hint":"Use defaultOption: \"decline\" (fail closed) or leave it out; ask an Owner to create a rule for pre-approval.","docUrl":"https://logu.dev/docs/errors#approving_default","requestId":"8c1f0b2a9d3e4f51-GRU","issues":[{"path":"defaultOption","message":"Can't be an approving option (id approve or style primary)"}]}}},"unknown_field":{"summary":"unknown_field (400)","description":"The body has a field the endpoint doesn't know (often a typo or snake_case).","value":{"error":{"code":"unknown_field","message":"Unknown field \"timeout_seconds\" (did you mean \"timeoutSeconds\"?)","hint":"Rename timeout_seconds to timeoutSeconds. Field names are camelCase; unknown fields are rejected, not ignored.","docUrl":"https://logu.dev/docs/errors#unknown_field","requestId":"8c1f0b2a9d3e4f51-GRU","issues":[{"path":"timeout_seconds","message":"Unknown field; did you mean \"timeoutSeconds\"?"}]}}},"unauthorized":{"summary":"unauthorized (401)","description":"No credentials, or the API key or session is invalid or revoked.","value":{"error":{"code":"unauthorized","message":"Missing or invalid credentials","hint":"Send `Authorization: Bearer lg_…` with an active API key (create one in Logu → Agents).","docUrl":"https://logu.dev/docs/errors#unauthorized","requestId":"8c1f0b2a9d3e4f51-GRU"}}},"forbidden":{"summary":"forbidden (403)","description":"The caller is authenticated but may not do this (wrong role, or an API key on a human-only endpoint).","value":{"error":{"code":"forbidden","message":"This endpoint requires a signed-in human, not an API key","hint":"Agents send asks, events and metrics; only a human Decider or Owner decides asks.","docUrl":"https://logu.dev/docs/errors#forbidden","requestId":"8c1f0b2a9d3e4f51-GRU"}}},"workspace_suspended":{"summary":"workspace_suspended (403)","description":"The workspace was suspended by Logu (usually for abuse or a violation of the terms). Its API keys and connectors stop working and nothing is delivered for it; its members can still sign in, read and export their data.","value":{"error":{"code":"workspace_suspended","message":"This workspace is suspended","hint":"Sign in at https://logu.dev to see the workspace, and contact Logu support to ask for a review.","docUrl":"https://logu.dev/docs/errors#workspace_suspended","requestId":"8c1f0b2a9d3e4f51-GRU"}}},"email_not_verified":{"summary":"email_not_verified (403)","description":"The signed-in human's email isn't verified yet, and this needs it (accepting an invitation, creating a shared workspace, or becoming the Owner of one).","value":{"error":{"code":"email_not_verified","message":"Confirm ana@example.com before joining: open the link Logu emails you","hint":"Confirm your email with the link Logu sends (Settings → Account, or the invitation page), then retry.","docUrl":"https://logu.dev/docs/errors#email_not_verified","requestId":"8c1f0b2a9d3e4f51-GRU"}}},"not_found":{"summary":"not_found (404)","description":"No such route, or the resource doesn't exist in your workspace.","value":{"error":{"code":"not_found","message":"Ask not found","hint":"Check the path and the id. API keys only see their own workspace.","docUrl":"https://logu.dev/docs/errors#not_found","requestId":"8c1f0b2a9d3e4f51-GRU"}}},"method_not_allowed":{"summary":"method_not_allowed (405)","description":"The path exists but not with this HTTP method.","value":{"error":{"code":"method_not_allowed","message":"PUT isn't allowed on /v1/asks","hint":"Use GET or POST.","docUrl":"https://logu.dev/docs/errors#method_not_allowed","requestId":"8c1f0b2a9d3e4f51-GRU"}}},"conflict":{"summary":"conflict (409)","description":"The 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.","value":{"error":{"code":"conflict","message":"This ask is no longer waiting: it was decided by Ana","hint":"It can't be decided or withdrawn anymore. Read error.ask.status and error.ask.decision instead of retrying.","docUrl":"https://logu.dev/docs/errors#conflict","requestId":"8c1f0b2a9d3e4f51-GRU","ask":{"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}}}},"idempotency_key_in_use":{"summary":"idempotency_key_in_use (409)","description":"A request with the same idempotency key is still being processed.","value":{"error":{"code":"idempotency_key_in_use","message":"A request with this idempotency key is still being processed","hint":"Retry in a moment with the same key to get its result.","docUrl":"https://logu.dev/docs/errors#idempotency_key_in_use","requestId":"8c1f0b2a9d3e4f51-GRU"}}},"idempotency_key_reused":{"summary":"idempotency_key_reused (422)","description":"The idempotency key was already used with a different request body or endpoint.","value":{"error":{"code":"idempotency_key_reused","message":"This idempotency key was already used with a different request body","hint":"Use a new key for a different request. Retrying with key \"deploy-4821\" only works with the exact same body (keys are per agent and remembered for 24 hours).","docUrl":"https://logu.dev/docs/errors#idempotency_key_reused","requestId":"8c1f0b2a9d3e4f51-GRU"}}},"rate_limited":{"summary":"rate_limited (429)","description":"This API key sent too many requests, or a human did too much too fast: sign-in attempts, workspaces created in a day, or a global daily cap on new accounts was reached.","value":{"error":{"code":"rate_limited","message":"Too many asks: this API key may send 60 per minute","hint":"Wait for `Retry-After` seconds, then retry. API key limits are per key, per minute.","docUrl":"https://logu.dev/docs/errors#rate_limited","requestId":"8c1f0b2a9d3e4f51-GRU"}}},"internal_error":{"summary":"internal_error (500)","description":"Something went wrong on Logu's side.","value":{"error":{"code":"internal_error","message":"Something went wrong","hint":"Retry with backoff. If it persists, report the `requestId`.","docUrl":"https://logu.dev/docs/errors#internal_error","requestId":"8c1f0b2a9d3e4f51-GRU"}}},"authorization_pending":{"summary":"authorization_pending (400)","description":"The device code is waiting for a human to approve it in the app (`POST /v1/device/token`).","value":{"error":{"code":"authorization_pending","message":"Waiting for a human to approve the code","hint":"Keep polling every `interval` seconds until the human approves or denies it, or it expires.","docUrl":"https://logu.dev/docs/errors#authorization_pending","requestId":"8c1f0b2a9d3e4f51-GRU"}}},"slow_down":{"summary":"slow_down (400)","description":"The device code was polled faster than its `interval`.","value":{"error":{"code":"slow_down","message":"Polling too fast: wait 10 seconds between polls","hint":"Wait 5 seconds longer between polls from now on, then poll again.","docUrl":"https://logu.dev/docs/errors#slow_down","requestId":"8c1f0b2a9d3e4f51-GRU"}}},"expired_token":{"summary":"expired_token (400)","description":"The device code expired (10 minutes) or was already used to get its API key.","value":{"error":{"code":"expired_token","message":"This device code expired or was already used","hint":"Start again: request a new device code (`logu login`).","docUrl":"https://logu.dev/docs/errors#expired_token","requestId":"8c1f0b2a9d3e4f51-GRU"}}},"access_denied":{"summary":"access_denied (403)","description":"The human denied the device code in the app.","value":{"error":{"code":"access_denied","message":"The human denied this request","hint":"Stop polling. Start again only if the human wants to connect this agent.","docUrl":"https://logu.dev/docs/errors#access_denied","requestId":"8c1f0b2a9d3e4f51-GRU"}}},"plan_limit":{"summary":"plan_limit (402)","description":"This is over one of the workspace's plan quotas: asks, events or metric reports this month, stored images, projects or metric names (Free and Pro), or on Free its agents, members and webhook endpoints (only on servers that enforce plan limits), or a Free account that already owns its shared workspaces (always).","value":{"error":{"code":"plan_limit","message":"This workspace is on the Free plan, which includes 100 asks a month, and it has used them all this month. Nothing was created.","hint":"An Owner can upgrade the workspace to Pro in Logu → Settings → Plan (on Pro, ask the Logu team to raise the quota); monthly counts reset on the 1st (UTC).","docUrl":"https://logu.dev/docs/errors#plan_limit","requestId":"8c1f0b2a9d3e4f51-GRU"}}},"ceiling_reached":{"summary":"ceiling_reached (402)","description":"The workspace reached one of Logu's cost ceilings (asks, events, metric reports, test traffic or images this month, stored image bytes, projects, metric names or API keys). Unlike `plan_limit`, upgrading doesn't lift it. An account that already owns the most shared workspaces an account may own gets it too.","value":{"error":{"code":"ceiling_reached","message":"This workspace reached Logu's cost ceiling of 20,000 asks a month. Nothing was created.","hint":"Nothing was created, and an ask that wasn't created was not asked: don't proceed as if a human had answered. Tell your human; monthly counts reset on the 1st (UTC), and an operator can raise a ceiling for a workspace. Don't retry before then.","docUrl":"https://logu.dev/docs/errors#ceiling_reached","requestId":"8c1f0b2a9d3e4f51-GRU"}}},"overloaded":{"summary":"overloaded (503)","description":"The workspace's realtime hub is overloaded, so this request (a long-poll wait or the stream) was shed.","value":{"error":{"code":"overloaded","message":"The workspace's realtime hub is overloaded, so this request was shed. Nothing was lost","hint":"Retry after `Retry-After` seconds, with jitter. Nothing was lost: asks, events, metrics and decisions don't depend on the hub.","docUrl":"https://logu.dev/docs/errors#overloaded","requestId":"8c1f0b2a9d3e4f51-GRU"}}},"service_unavailable":{"summary":"service_unavailable (503)","description":"A dependency is down or a feature isn't configured on this server.","value":{"error":{"code":"service_unavailable","message":"Telegram isn't configured on this server","hint":"Retry with backoff; for a feature that isn't configured, ask the server's operator.","docUrl":"https://logu.dev/docs/errors#service_unavailable","requestId":"8c1f0b2a9d3e4f51-GRU"}}}},"responses":{"BadRequestOrUnknownFieldOrApprovingDefault":{"description":"`bad_request`: The request is malformed or a field is invalid (see `issues`).\n\n`unknown_field`: The body has a field the endpoint doesn't know (often a typo or snake_case).\n\n`approving_default`: The ask's `defaultOption` is an approving option (the id `approve` or a `primary` style): an agent can't pre-approve by default.","headers":{"Logu-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"bad_request":{"$ref":"#/components/examples/bad_request"},"unknown_field":{"$ref":"#/components/examples/unknown_field"},"approving_default":{"$ref":"#/components/examples/approving_default"}}}}},"Unauthorized":{"description":"`unauthorized`: No credentials, or the API key or session is invalid or revoked.","headers":{"Logu-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"unauthorized":{"$ref":"#/components/examples/unauthorized"}}}}},"PlanLimitOrCeilingReached":{"description":"`plan_limit`: This is over one of the workspace's plan quotas: asks, events or metric reports this month, stored images, projects or metric names (Free and Pro), or on Free its agents, members and webhook endpoints (only on servers that enforce plan limits), or a Free account that already owns its shared workspaces (always).\n\n`ceiling_reached`: The workspace reached one of Logu's cost ceilings (asks, events, metric reports, test traffic or images this month, stored image bytes, projects, metric names or API keys). Unlike `plan_limit`, upgrading doesn't lift it. An account that already owns the most shared workspaces an account may own gets it too.","headers":{"Logu-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"plan_limit":{"$ref":"#/components/examples/plan_limit"},"ceiling_reached":{"$ref":"#/components/examples/ceiling_reached"}}}}},"ForbiddenOrWorkspaceSuspended":{"description":"`forbidden`: The caller is authenticated but may not do this (wrong role, or an API key on a human-only endpoint).\n\n`workspace_suspended`: The workspace was suspended by Logu (usually for abuse or a violation of the terms). Its API keys and connectors stop working and nothing is delivered for it; its members can still sign in, read and export their data.","headers":{"Logu-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"forbidden":{"$ref":"#/components/examples/forbidden"},"workspace_suspended":{"$ref":"#/components/examples/workspace_suspended"}}}}},"IdempotencyKeyInUse":{"description":"`idempotency_key_in_use`: A request with the same idempotency key is still being processed.","headers":{"Logu-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"idempotency_key_in_use":{"$ref":"#/components/examples/idempotency_key_in_use"}}}}},"IdempotencyKeyReused":{"description":"`idempotency_key_reused`: The idempotency key was already used with a different request body or endpoint.","headers":{"Logu-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"idempotency_key_reused":{"$ref":"#/components/examples/idempotency_key_reused"}}}}},"RateLimited":{"description":"`rate_limited`: This API key sent too many requests, or a human did too much too fast: sign-in attempts, workspaces created in a day, or a global daily cap on new accounts was reached.","headers":{"Logu-Request-Id":{"$ref":"#/components/headers/RequestId"},"Retry-After":{"$ref":"#/components/headers/RetryAfter"},"RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"},"RateLimit-Policy":{"$ref":"#/components/headers/RateLimitPolicy"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"rate_limited":{"$ref":"#/components/examples/rate_limited"}}}}},"InternalError":{"description":"`internal_error`: Something went wrong on Logu's side.","headers":{"Logu-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"internal_error":{"$ref":"#/components/examples/internal_error"}}}}},"ServiceUnavailable":{"description":"`service_unavailable`: A dependency is down or a feature isn't configured on this server.","headers":{"Logu-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"service_unavailable":{"$ref":"#/components/examples/service_unavailable"}}}}},"BadRequest":{"description":"`bad_request`: The request is malformed or a field is invalid (see `issues`).","headers":{"Logu-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"bad_request":{"$ref":"#/components/examples/bad_request"}}}}},"NotFound":{"description":"`not_found`: No such route, or the resource doesn't exist in your workspace.","headers":{"Logu-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"not_found":{"$ref":"#/components/examples/not_found"}}}}},"Forbidden":{"description":"`forbidden`: The caller is authenticated but may not do this (wrong role, or an API key on a human-only endpoint).","headers":{"Logu-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"forbidden":{"$ref":"#/components/examples/forbidden"}}}}},"Overloaded":{"description":"`overloaded`: The workspace's realtime hub is overloaded, so this request (a long-poll wait or the stream) was shed.","headers":{"Logu-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"overloaded":{"$ref":"#/components/examples/overloaded"}}}}},"Conflict":{"description":"`conflict`: The 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.","headers":{"Logu-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"conflict":{"$ref":"#/components/examples/conflict"}}}}},"BadRequestOrUnknownField":{"description":"`bad_request`: The request is malformed or a field is invalid (see `issues`).\n\n`unknown_field`: The body has a field the endpoint doesn't know (often a typo or snake_case).","headers":{"Logu-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"bad_request":{"$ref":"#/components/examples/bad_request"},"unknown_field":{"$ref":"#/components/examples/unknown_field"}}}}},"WorkspaceSuspended":{"description":"`workspace_suspended`: The workspace was suspended by Logu (usually for abuse or a violation of the terms). Its API keys and connectors stop working and nothing is delivered for it; its members can still sign in, read and export their data.","headers":{"Logu-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"workspace_suspended":{"$ref":"#/components/examples/workspace_suspended"}}}}},"PlanLimit":{"description":"`plan_limit`: This is over one of the workspace's plan quotas: asks, events or metric reports this month, stored images, projects or metric names (Free and Pro), or on Free its agents, members and webhook endpoints (only on servers that enforce plan limits), or a Free account that already owns its shared workspaces (always).","headers":{"Logu-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"plan_limit":{"$ref":"#/components/examples/plan_limit"}}}}},"CeilingReached":{"description":"`ceiling_reached`: The workspace reached one of Logu's cost ceilings (asks, events, metric reports, test traffic or images this month, stored image bytes, projects, metric names or API keys). Unlike `plan_limit`, upgrading doesn't lift it. An account that already owns the most shared workspaces an account may own gets it too.","headers":{"Logu-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"ceiling_reached":{"$ref":"#/components/examples/ceiling_reached"}}}}},"BadRequestOrUnknownFieldOrAuthorizationPendingOrSlowDownOrExpiredToken":{"description":"`bad_request`: The request is malformed or a field is invalid (see `issues`).\n\n`unknown_field`: The body has a field the endpoint doesn't know (often a typo or snake_case).\n\n`authorization_pending`: The device code is waiting for a human to approve it in the app (`POST /v1/device/token`).\n\n`slow_down`: The device code was polled faster than its `interval`.\n\n`expired_token`: The device code expired (10 minutes) or was already used to get its API key.","headers":{"Logu-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"bad_request":{"$ref":"#/components/examples/bad_request"},"unknown_field":{"$ref":"#/components/examples/unknown_field"},"authorization_pending":{"$ref":"#/components/examples/authorization_pending"},"slow_down":{"$ref":"#/components/examples/slow_down"},"expired_token":{"$ref":"#/components/examples/expired_token"}}}}},"AccessDenied":{"description":"`access_denied`: The human denied the device code in the app.","headers":{"Logu-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"access_denied":{"$ref":"#/components/examples/access_denied"}}}}},"ForbiddenOrEmailNotVerified":{"description":"`forbidden`: The caller is authenticated but may not do this (wrong role, or an API key on a human-only endpoint).\n\n`email_not_verified`: The signed-in human's email isn't verified yet, and this needs it (accepting an invitation, creating a shared workspace, or becoming the Owner of one).","headers":{"Logu-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"forbidden":{"$ref":"#/components/examples/forbidden"},"email_not_verified":{"$ref":"#/components/examples/email_not_verified"}}}}},"UnknownFieldOrBadRequest":{"description":"`unknown_field`: The body has a field the endpoint doesn't know (often a typo or snake_case).\n\n`bad_request`: The request is malformed or a field is invalid (see `issues`).","headers":{"Logu-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"unknown_field":{"$ref":"#/components/examples/unknown_field"},"bad_request":{"$ref":"#/components/examples/bad_request"}}}}}},"schemas":{"AskOption":{"type":"object","properties":{"id":{"type":"string","minLength":1,"maxLength":64,"pattern":"^[a-z0-9][a-z0-9._-]*$"},"label":{"type":"string","minLength":1,"maxLength":64},"style":{"type":"string","enum":["primary","danger","default"]}},"required":["id","label","style"],"description":"A button shown to the human."},"AskStatus":{"type":"string","enum":["waiting","decided","timed_out","withdrawn"],"description":"`waiting` (not yet decided), `decided` (a human chose, or the default applied on expiry), `timed_out` (expired with no default) or `withdrawn` (the agent took it back)."},"MemberRef":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"}},"required":["id","name"],"description":"A member, as shown next to what they did."},"Decision":{"type":"object","properties":{"optionId":{"type":["string","null"]},"text":{"type":["string","null"]},"decidedVia":{"$ref":"#/components/schemas/DecidedVia"},"decidedAt":{"type":"string","format":"date-time"},"decidedBy":{"anyOf":[{"$ref":"#/components/schemas/MemberRef"},{"type":"null"}]},"ruleId":{"type":["string","null"]}},"required":["optionId","text","decidedVia","decidedAt","decidedBy"],"description":"What the human returned: the option and/or text, who, when and where."},"DecidedVia":{"type":"string","enum":["app","link","telegram","slack","whatsapp","rule","timeout"],"description":"Where it was decided. `timeout` means nobody did: the ask's default option was applied. Never treat that as approval, even if `optionId` is `approve` (asks created before the policy may still carry such a default); check this field."},"AgentRef":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"}},"required":["id","name"],"description":"The agent an ask, event or metric came from."},"Attachment":{"type":"object","properties":{"id":{"type":"string"},"url":{"type":"string","description":"A 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}`."},"contentType":{"type":"string","enum":["image/png","image/jpeg","image/webp","image/gif"]},"size":{"type":"integer","description":"Bytes."},"width":{"type":["integer","null"],"description":"Pixels, when Logu could read them from the image's header."},"height":{"type":["integer","null"],"description":"Pixels, when Logu could read them from the image's header."},"name":{"type":["string","null"],"description":"The file name it was uploaded with, if any (the image's alt text in the app)."}},"required":["id","url","contentType","size","width","height","name"],"description":"An image on an ask or event, with a signed `url` that works for at least an hour."},"Ask":{"type":"object","properties":{"id":{"type":"string"},"project":{"type":"string"},"channel":{"type":["string","null"]},"title":{"type":"string"},"body":{"type":["string","null"]},"icon":{"type":["string","null"]},"url":{"type":["string","null"]},"options":{"type":"array","items":{"$ref":"#/components/schemas/AskOption"}},"allowText":{"type":"boolean"},"status":{"$ref":"#/components/schemas/AskStatus"},"decision":{"anyOf":[{"$ref":"#/components/schemas/Decision"},{"type":"null"}]},"defaultOption":{"type":["string","null"],"description":"Applied 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."},"expiresAt":{"type":"string","format":"date-time"},"createdAt":{"type":"string","format":"date-time"},"metadata":{"type":["object","null"],"additionalProperties":{},"description":"Free-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."},"agent":{"anyOf":[{"$ref":"#/components/schemas/AgentRef"},{"type":"null"}]},"attachments":{"type":"array","items":{"$ref":"#/components/schemas/Attachment"},"description":"Its images, in order, each with a signed `url` that works for about an hour."},"test":{"type":"boolean","description":"Sent 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."},"escalation":{"type":["object","null"],"properties":{"at":{"type":"string","format":"date-time"},"to":{"type":"array","items":{"$ref":"#/components/schemas/MemberRef"}}},"required":["at","to"]}},"required":["id","project","channel","title","body","icon","url","options","allowText","status","decision","expiresAt","createdAt","metadata","agent","attachments","test"],"description":"An agent's request for a human decision, as it is now."},"AskOptionInput":{"type":"object","properties":{"id":{"type":"string","minLength":1,"maxLength":64,"pattern":"^[a-z0-9][a-z0-9._-]*$"},"label":{"type":"string","minLength":1,"maxLength":64},"style":{"type":"string","enum":["primary","danger","default"],"default":"default"}},"required":["id","label"],"additionalProperties":false,"description":"A button as the agent sends it."},"AskInput":{"type":"object","properties":{"project":{"type":"string","minLength":1,"maxLength":64,"pattern":"^[a-z0-9][a-z0-9._-]*$"},"channel":{"type":"string","minLength":1,"maxLength":64,"pattern":"^[a-z0-9][a-z0-9._-]*$"},"title":{"type":"string","minLength":1,"maxLength":200},"body":{"type":"string","maxLength":10000},"icon":{"type":"string","minLength":1,"maxLength":16},"url":{"type":"string","format":"uri"},"options":{"type":"array","items":{"$ref":"#/components/schemas/AskOptionInput"},"minItems":1,"maxItems":6},"allowText":{"type":"boolean","default":false},"timeoutSeconds":{"type":"integer","minimum":10,"maximum":2592000,"default":86400},"defaultOption":{"type":"string","minLength":1,"maxLength":64,"pattern":"^[a-z0-9][a-z0-9._-]*$"},"callbackUrl":{"type":"string","maxLength":2048,"format":"uri"},"idempotencyKey":{"type":"string","minLength":1,"maxLength":200},"metadata":{"type":"object","additionalProperties":{},"description":"Free-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."},"attachments":{"type":"array","items":{"type":"string","pattern":"^att_[0-9A-Z]{26}$"},"maxItems":4}},"required":["project","title"],"additionalProperties":false,"description":"What an agent sends to ask. Strict: an unknown field is a 400 `unknown_field`."},"UploadedAttachment":{"type":"object","properties":{"id":{"type":"string"},"url":{"type":"string","description":"A 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}`."},"contentType":{"type":"string","enum":["image/png","image/jpeg","image/webp","image/gif"]},"size":{"type":"integer","description":"Bytes."},"width":{"type":["integer","null"],"description":"Pixels, when Logu could read them from the image's header."},"height":{"type":["integer","null"],"description":"Pixels, when Logu could read them from the image's header."},"name":{"type":["string","null"],"description":"The file name it was uploaded with, if any (the image's alt text in the app)."},"expiresAt":{"type":"string","format":"date-time","description":"When it's deleted if no ask or event uses it by then."}},"required":["id","url","contentType","size","width","height","name","expiresAt"],"description":"An uploaded image, until an ask or event uses it (else deleted at `expiresAt`)."},"DecisionInput":{"type":"object","properties":{"optionId":{"type":"string","minLength":1,"maxLength":64,"pattern":"^[a-z0-9][a-z0-9._-]*$"},"text":{"type":"string","minLength":1,"maxLength":5000}},"additionalProperties":false,"description":"A human's decision: an option, a text, or both."},"Event":{"type":"object","properties":{"id":{"type":"string"},"project":{"type":"string"},"channel":{"type":["string","null"]},"title":{"type":"string"},"body":{"type":["string","null"]},"icon":{"type":["string","null"]},"url":{"type":["string","null"]},"metadata":{"type":["object","null"],"additionalProperties":{},"description":"Free-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."},"agent":{"anyOf":[{"$ref":"#/components/schemas/AgentRef"},{"type":"null"}]},"attachments":{"type":"array","items":{"$ref":"#/components/schemas/Attachment"},"description":"Its images, in order, each with a signed `url` that works for about an hour."},"test":{"type":"boolean","description":"Sent with a test API key (`lg_test_…`): shown in the app, never delivered."},"createdAt":{"type":"string","format":"date-time"}},"required":["id","project","channel","title","body","icon","url","metadata","agent","attachments","test","createdAt"],"description":"Something an agent reported that needs no action."},"EventInput":{"type":"object","properties":{"project":{"type":"string","minLength":1,"maxLength":64,"pattern":"^[a-z0-9][a-z0-9._-]*$"},"channel":{"type":"string","minLength":1,"maxLength":64,"pattern":"^[a-z0-9][a-z0-9._-]*$"},"title":{"type":"string","minLength":1,"maxLength":200},"body":{"type":"string","maxLength":10000},"icon":{"type":"string","minLength":1,"maxLength":16},"url":{"type":"string","format":"uri"},"metadata":{"type":"object","additionalProperties":{},"description":"Free-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."},"attachments":{"type":"array","items":{"type":"string","pattern":"^att_[0-9A-Z]{26}$"},"maxItems":4}},"required":["project","title"],"additionalProperties":false,"description":"What an agent sends to report an event. Strict."},"MetricInput":{"type":"object","properties":{"project":{"type":"string","minLength":1,"maxLength":64,"pattern":"^[a-z0-9][a-z0-9._-]*$"},"metric":{"type":"string","minLength":1,"maxLength":64,"pattern":"^[a-z0-9][a-z0-9._-]*$"},"value":{"type":"number"},"unit":{"type":"string","maxLength":16},"label":{"type":"string","minLength":1,"maxLength":64}},"required":["project","metric","value"],"additionalProperties":false,"description":"A metric value an agent (or a Decider) reports. Strict."},"MetricPoint":{"type":"object","properties":{"value":{"type":"number"},"at":{"type":"string","format":"date-time"}},"required":["value","at"],"description":"One reported value."},"MetricAlert":{"type":"object","properties":{"id":{"type":"string"},"condition":{"type":"string","enum":["above","below"]},"threshold":{"type":"number"},"triggered":{"type":"boolean"}},"required":["id","condition","threshold","triggered"],"description":"A threshold alert: an event when the metric goes above or below a value."},"Metric":{"type":"object","properties":{"id":{"type":"string"},"project":{"type":"string"},"metric":{"type":"string"},"label":{"type":["string","null"]},"unit":{"type":["string","null"]},"value":{"type":"number"},"at":{"type":"string","format":"date-time"},"points":{"type":"array","items":{"$ref":"#/components/schemas/MetricPoint"}},"alerts":{"type":"array","items":{"$ref":"#/components/schemas/MetricAlert"}},"agent":{"anyOf":[{"$ref":"#/components/schemas/AgentRef"},{"type":"null"}]},"test":{"type":"boolean","description":"Reported with a test API key (`lg_test_…`): kept apart from the live metric of the same name, not kept in the long-term history, and its threshold alerts create test events."}},"required":["id","project","metric","label","unit","value","at","points","alerts","agent","test"],"description":"A named number reported over time, with recent points and threshold alerts."},"RecordedMetric":{"type":"object","properties":{"id":{"type":"string"},"project":{"type":"string"},"metric":{"type":"string"},"label":{"type":["string","null"]},"unit":{"type":["string","null"]},"value":{"type":"number"},"at":{"type":"string","format":"date-time"},"agent":{"anyOf":[{"$ref":"#/components/schemas/AgentRef"},{"type":"null"}]},"test":{"type":"boolean","description":"Reported with a test API key (`lg_test_…`): kept apart from the live metric of the same name, not kept in the long-term history, and its threshold alerts create test events."}},"required":["id","project","metric","label","unit","value","at","agent","test"],"description":"A metric with its latest value, as just reported."},"CreateMetricAlertInput":{"type":"object","properties":{"condition":{"type":"string","enum":["above","below"]},"threshold":{"type":"number"}},"required":["condition","threshold"],"additionalProperties":false,"description":"A new threshold alert. Strict."},"AskStats":{"type":"object","properties":{"waiting":{"type":"integer"},"decided7d":{"type":"integer"},"timedOut7d":{"type":"integer"},"medianDecisionSeconds":{"type":["number","null"]}},"required":["waiting","decided7d","timedOut7d","medianDecisionSeconds"],"description":"How quickly the workspace's humans decide."},"Insights":{"type":"object","properties":{"metrics":{"type":"array","items":{"$ref":"#/components/schemas/Metric"}},"asks":{"$ref":"#/components/schemas/AskStats"}},"required":["metrics","asks"],"description":"Metrics and ask stats: the app's Insights section."},"Project":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string","minLength":1,"maxLength":64,"pattern":"^[a-z0-9][a-z0-9._-]*$"},"muted":{"type":"boolean"},"routing":{"type":"object","properties":{"notify":{"type":["array","null"],"items":{"type":"string"}},"escalation":{"oneOf":[{"type":"object","properties":{"afterMinutes":{"type":"integer","minimum":1,"maximum":43200},"to":{"type":"string","enum":["owners"]}},"required":["afterMinutes","to"],"additionalProperties":false},{"type":"object","properties":{"afterMinutes":{"type":"integer","minimum":1,"maximum":43200},"to":{"type":"string","enum":["members"]},"members":{"type":"array","items":{"type":"string","minLength":1},"minItems":1,"maxItems":50}},"required":["afterMinutes","to","members"],"additionalProperties":false},{"type":"null"}]}},"required":["notify","escalation"]}},"required":["id","name","muted"],"description":"The top-level grouping agents send to, inside a workspace."},"UpdateProjectInput":{"type":"object","properties":{"muted":{"type":"boolean"}},"required":["muted"],"additionalProperties":false,"description":"Mute or unmute a project for yourself. Strict."},"ApiKey":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"prefix":{"type":"string"},"test":{"type":"boolean"},"createdAt":{"type":"string","format":"date-time"},"lastUsedAt":{"type":["string","null"],"format":"date-time"},"connector":{"type":["object","null"],"properties":{"clientId":{"type":"string"},"name":{"type":"string"}},"required":["clientId","name"]}},"required":["id","name","prefix","test","createdAt","lastUsedAt","connector"],"description":"An API key as listed: never its secret."},"Agent":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"notificationLevel":{"type":"string","enum":["asks_and_events","asks_only","silent"]},"createdAt":{"type":"string","format":"date-time"},"apiKeys":{"type":"array","items":{"$ref":"#/components/schemas/ApiKey"}},"asks30d":{"type":"integer"},"events30d":{"type":"integer"}},"required":["id","name","notificationLevel","createdAt","apiKeys","asks30d","events30d"],"description":"Anything that calls Logu from code, with its active API keys."},"NewApiKey":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"prefix":{"type":"string"},"test":{"type":"boolean"},"createdAt":{"type":"string","format":"date-time"},"lastUsedAt":{"type":["string","null"],"format":"date-time"},"connector":{"type":["object","null"],"properties":{"clientId":{"type":"string"},"name":{"type":"string"}},"required":["clientId","name"]},"key":{"type":"string"}},"required":["id","name","prefix","test","createdAt","lastUsedAt","connector","key"],"description":"A new API key with its secret (`lg_…`), shown this once."},"CreateAgentInput":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":64},"test":{"type":"boolean"}},"required":["name"],"additionalProperties":false,"description":"A new agent. Strict."},"CreateAgentResult":{"type":"object","properties":{"agent":{"$ref":"#/components/schemas/Agent"},"apiKey":{"$ref":"#/components/schemas/NewApiKey"}},"required":["agent","apiKey"],"description":"The new agent and its first API key."},"CreateApiKeyInput":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":64},"test":{"type":"boolean"}},"additionalProperties":false,"description":"Another API key for an agent. Strict."},"AgentMe":{"type":"object","properties":{"kind":{"type":"string","enum":["agent"]},"agent":{"$ref":"#/components/schemas/AgentRef"},"apiKey":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"prefix":{"type":"string"},"test":{"type":"boolean"}},"required":["id","name","prefix","test"]},"workspace":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"}},"required":["id","name"]},"limits":{"type":"object","additionalProperties":{"type":"number"}},"locale":{"type":["string","null"],"enum":["en","pt-BR",null]}},"required":["kind","agent","apiKey","workspace","limits","locale"],"description":"`GET /v1/me` with an API key: the agent, its workspace and its limits."},"HumanMe":{"type":"object","properties":{"kind":{"type":"string","enum":["human"]},"id":{"type":"string"},"name":{"type":"string"},"email":{"type":"string"},"emailVerified":{"type":"boolean"},"locale":{"type":["string","null"],"enum":["en","pt-BR",null],"description":"The language of the human's notifications and emails, sent by the app (`PATCH /v1/preferences`); null means English."}},"required":["kind","id","name","email","emailVerified","locale"],"description":"`GET /v1/me` with a session: the human's account."},"WebhookEventType":{"type":"string","enum":["ask.created","ask.settled","event.created","metric.alert"],"description":"What a webhook endpoint can subscribe to."},"WebhookEndpoint":{"type":"object","properties":{"id":{"type":"string"},"url":{"type":"string"},"description":{"type":["string","null"]},"eventTypes":{"type":"array","items":{"$ref":"#/components/schemas/WebhookEventType"}},"enabled":{"type":"boolean"},"disabledReason":{"type":["string","null"],"enum":["manual","failing",null]},"failingSince":{"type":["string","null"],"format":"date-time"},"previousSecretExpiresAt":{"type":["string","null"],"format":"date-time"},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}},"required":["id","url","description","eventTypes","enabled","disabledReason","failingSince","previousSecretExpiresAt","createdAt","updatedAt"],"description":"Where the workspace's webhooks go: a URL, the event types it gets, enabled or not. Never its signing secret."},"WebhookEndpointWithSecret":{"type":"object","properties":{"id":{"type":"string"},"url":{"type":"string"},"description":{"type":["string","null"]},"eventTypes":{"type":"array","items":{"$ref":"#/components/schemas/WebhookEventType"}},"enabled":{"type":"boolean"},"disabledReason":{"type":["string","null"],"enum":["manual","failing",null]},"failingSince":{"type":["string","null"],"format":"date-time"},"previousSecretExpiresAt":{"type":["string","null"],"format":"date-time"},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"},"secret":{"type":"string"}},"required":["id","url","description","eventTypes","enabled","disabledReason","failingSince","previousSecretExpiresAt","createdAt","updatedAt","secret"],"description":"A new endpoint, or one whose secret was rotated, with its signing secret (`whsec_…`), shown this once."},"CreateWebhookEndpointInput":{"type":"object","properties":{"url":{"type":"string","maxLength":2048,"format":"uri"},"description":{"type":"string","maxLength":200},"eventTypes":{"type":"array","items":{"$ref":"#/components/schemas/WebhookEventType"},"minItems":1,"maxItems":4},"enabled":{"type":"boolean","default":true}},"required":["url","eventTypes"],"additionalProperties":false,"description":"A new webhook endpoint: a public `https:` URL and the event types it gets. Strict."},"UpdateWebhookEndpointInput":{"type":"object","properties":{"url":{"type":"string","maxLength":2048,"format":"uri"},"description":{"type":["string","null"],"maxLength":200},"eventTypes":{"type":"array","items":{"$ref":"#/components/schemas/WebhookEventType"},"minItems":1,"maxItems":4},"enabled":{"type":"boolean"}},"additionalProperties":false,"description":"What changes on a webhook endpoint. Strict."},"WebhookDeliveryStatus":{"type":"string","enum":["pending","succeeded","failed"],"description":"`pending` while attempts remain, then `succeeded` (a 2xx) or `failed`."},"WebhookDelivery":{"type":"object","properties":{"id":{"type":"string"},"endpointId":{"type":"string"},"type":{"type":"string","enum":["ask.created","ask.settled","event.created","metric.alert","webhook.test"]},"status":{"$ref":"#/components/schemas/WebhookDeliveryStatus"},"attemptCount":{"type":"integer"},"lastStatusCode":{"type":["integer","null"]},"lastAttemptAt":{"type":["string","null"],"format":"date-time"},"nextAttemptAt":{"type":["string","null"],"format":"date-time"},"createdAt":{"type":"string","format":"date-time"}},"required":["id","endpointId","type","status","attemptCount","lastStatusCode","lastAttemptAt","nextAttemptAt","createdAt"],"description":"One message to one endpoint, as the delivery log lists it. Its `id` is the `webhook-id` header."},"WebhookAttempt":{"type":"object","properties":{"number":{"type":"integer"},"at":{"type":"string","format":"date-time"},"statusCode":{"type":["integer","null"]},"durationMs":{"type":"integer"},"response":{"type":["string","null"]},"error":{"type":["string","null"]}},"required":["number","at","statusCode","durationMs","response","error"],"description":"One try at a webhook delivery: the receiver's status and the start of its response."},"WebhookDeliveryDetail":{"type":"object","properties":{"id":{"type":"string"},"endpointId":{"type":"string"},"type":{"type":"string","enum":["ask.created","ask.settled","event.created","metric.alert","webhook.test"]},"status":{"$ref":"#/components/schemas/WebhookDeliveryStatus"},"attemptCount":{"type":"integer"},"lastStatusCode":{"type":["integer","null"]},"lastAttemptAt":{"type":["string","null"],"format":"date-time"},"nextAttemptAt":{"type":["string","null"],"format":"date-time"},"createdAt":{"type":"string","format":"date-time"},"payload":{},"attempts":{"type":"array","items":{"$ref":"#/components/schemas/WebhookAttempt"}}},"required":["id","endpointId","type","status","attemptCount","lastStatusCode","lastAttemptAt","nextAttemptAt","createdAt","attempts"],"description":"One webhook delivery with the exact payload sent and every attempt."},"ActivityAction":{"type":"string","enum":["ask.created","ask.decided","ask.withdrawn","ask.timed_out","ask.escalated","agent.created","agent.updated","agent.deleted","agent.connected","api_key.created","api_key.revoked","device.registered","device.removed","browser_push.enabled","browser_push.disabled","telegram.connected","telegram.disconnected","slack.connected","slack.disconnected","whatsapp.connected","whatsapp.disconnected","preferences.updated","delivery.tested","project.muted","project.unmuted","project.routing_changed","rule.created","rule.deleted","metric.deleted","alert.created","alert.deleted","webhook.created","webhook.updated","webhook.deleted","webhook.secret_rotated","webhook.redelivered","webhook.disabled","workspace.created","workspace.create_refused","workspace.renamed","workspace.suspended","workspace.unsuspended","member.invited","member.joined","member.role_changed","member.removed","invitation.revoked","sign_in_method.added","sign_in_method.removed","session.revoked","billing.checkout_started","billing.subscribed","billing.updated","billing.canceled","billing.payment_failed","billing.quota_warning","billing.quota_reached","billing.ceiling_reached","delivery.capped","account.deletion_requested","account.restored","account.exported"],"description":"What happened."},"ActivityActor":{"oneOf":[{"type":"object","properties":{"kind":{"type":"string","enum":["human"]},"user":{"anyOf":[{"$ref":"#/components/schemas/MemberRef"},{"type":"null"}]}},"required":["kind","user"]},{"type":"object","properties":{"kind":{"type":"string","enum":["agent"]},"apiKeyId":{"type":"string"},"apiKeyName":{"type":["string","null"]},"agent":{"anyOf":[{"$ref":"#/components/schemas/AgentRef"},{"type":"null"}]}},"required":["kind","apiKeyId","apiKeyName","agent"]},{"type":"object","properties":{"kind":{"type":"string","enum":["system"]}},"required":["kind"]}],"description":"Who did it: a member, an agent's API key, or Logu."},"ActivityEntry":{"type":"object","properties":{"id":{"type":"string"},"workspaceId":{"type":"string"},"action":{"$ref":"#/components/schemas/ActivityAction"},"actor":{"$ref":"#/components/schemas/ActivityActor"},"target":{"type":["object","null"],"properties":{"type":{"type":"string"},"id":{"type":"string"}},"required":["type","id"]},"data":{"type":["object","null"],"additionalProperties":{}},"createdAt":{"type":"string","format":"date-time"}},"required":["id","workspaceId","action","actor","target","data","createdAt"],"description":"One entry of the activity log: who did what, when, where."},"DevicePlatform":{"type":"string","enum":["ios","android","web"],"description":"The kind of device registered for push."},"RegisterDeviceInput":{"type":"object","properties":{"platform":{"$ref":"#/components/schemas/DevicePlatform"},"token":{"type":"string","minLength":20,"maxLength":4096}},"required":["platform","token"],"additionalProperties":false,"description":"A phone's FCM token. Strict."},"WebPushSubscriptionInput":{"type":"object","properties":{"endpoint":{"type":"string","maxLength":2048,"format":"uri"},"expirationTime":{"type":["number","null"]},"keys":{"type":"object","properties":{"p256dh":{"type":"string","minLength":80,"maxLength":100},"auth":{"type":"string","minLength":16,"maxLength":32}},"required":["p256dh","auth"],"additionalProperties":false}},"required":["endpoint","keys"],"additionalProperties":false,"description":"A browser's `PushSubscription.toJSON()`. Strict."},"UnsubscribeWebPushInput":{"type":"object","properties":{"endpoint":{"type":"string","maxLength":2048}},"required":["endpoint"],"additionalProperties":false,"description":"The browser to unsubscribe. Strict."},"TelegramStatus":{"type":"object","properties":{"available":{"type":"boolean"},"connected":{"type":"boolean"},"username":{"type":["string","null"]}},"required":["available","connected","username"],"description":"The member's Telegram delivery."},"TelegramLink":{"type":"object","properties":{"url":{"type":"string","format":"uri"},"expiresAt":{"type":"string","format":"date-time"}},"required":["url","expiresAt"],"description":"A one-time t.me link that connects a Telegram chat."},"SlackStatus":{"type":"object","properties":{"available":{"type":"boolean"},"connected":{"type":"boolean"},"team":{"type":["string","null"]}},"required":["available","connected","team"],"description":"The member's Slack delivery."},"SlackInstall":{"type":"object","properties":{"url":{"type":"string","format":"uri"},"expiresAt":{"type":"string","format":"date-time"}},"required":["url","expiresAt"],"description":"Slack's \"Add to Slack\" page that connects the member's DM."},"WhatsAppStatus":{"type":"object","properties":{"available":{"type":"boolean"},"connected":{"type":"boolean"},"number":{"type":["string","null"]}},"required":["available","connected","number"],"description":"The member's WhatsApp delivery."},"WhatsAppLink":{"type":"object","properties":{"url":{"type":"string","format":"uri"},"expiresAt":{"type":"string","format":"date-time"}},"required":["url","expiresAt"],"description":"A one-time wa.me link that connects a WhatsApp number."},"QuietHours":{"type":"object","properties":{"start":{"type":"string","pattern":"^([01]\\d|2[0-3]):[0-5]\\d$"},"end":{"type":"string","pattern":"^([01]\\d|2[0-3]):[0-5]\\d$"},"timeZone":{"type":"string"}},"required":["start","end","timeZone"],"description":"A daily window when deliveries are held."},"Preferences":{"type":"object","properties":{"emailAsks":{"type":"boolean"},"quietHours":{"anyOf":[{"$ref":"#/components/schemas/QuietHours"},{"type":"null"}]},"quietAsks":{"type":"boolean"},"locale":{"type":["string","null"],"enum":["en","pt-BR",null],"description":"The language of the member's push, browser notifications, Telegram and Slack messages and emails (`en`, `pt-BR`); null means English. The app sends its own on sign-in and when it changes."},"emailAvailable":{"type":"boolean","description":"Whether this server can send email (else `emailAsks` does nothing)."}},"required":["emailAsks","quietHours","quietAsks","locale","emailAvailable"],"description":"A member's delivery settings (the same in every workspace)."},"UpdatePreferencesInput":{"type":"object","properties":{"emailAsks":{"type":"boolean"},"quietHours":{"type":["object","null"],"properties":{"start":{"type":"string","pattern":"^([01]\\d|2[0-3]):[0-5]\\d$"},"end":{"type":"string","pattern":"^([01]\\d|2[0-3]):[0-5]\\d$"},"timeZone":{"type":"string"}},"required":["start","end","timeZone"],"additionalProperties":false},"quietAsks":{"type":"boolean"},"locale":{"type":"string","enum":["en","pt-BR"]}},"additionalProperties":false,"description":"The delivery settings that change. Strict."},"WorkspaceRole":{"type":"string","enum":["owner","decider","viewer"],"description":"`owner` (everything), `decider` (decides asks; manages agents, projects, threshold alerts) or `viewer` (sees, never decides)."},"Workspace":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"personal":{"type":"boolean"},"role":{"$ref":"#/components/schemas/WorkspaceRole"},"plan":{"type":"string","enum":["free","pro"]},"createdAt":{"type":"string","format":"date-time"}},"required":["id","name","personal","role","plan","createdAt"],"description":"A workspace as one of its members sees it."},"CreateWorkspaceInput":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":64}},"required":["name"],"additionalProperties":false,"description":"A new shared workspace. Strict."},"UpdateWorkspaceInput":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":64}},"required":["name"],"additionalProperties":false,"description":"A workspace's new name. Strict."},"Member":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"email":{"type":"string"},"role":{"$ref":"#/components/schemas/WorkspaceRole"},"joinedAt":{"type":"string","format":"date-time"}},"required":["id","name","email","role","joinedAt"],"description":"A human in a workspace, with one role."},"UpdateMemberInput":{"type":"object","properties":{"role":{"$ref":"#/components/schemas/WorkspaceRole"}},"required":["role"],"additionalProperties":false,"description":"A member's new role. Strict."},"InvitationStatus":{"type":"string","enum":["pending","accepted","revoked","expired"],"description":"Where an invitation stands."},"Invitation":{"type":"object","properties":{"id":{"type":"string"},"email":{"type":"string"},"role":{"$ref":"#/components/schemas/WorkspaceRole"},"status":{"$ref":"#/components/schemas/InvitationStatus"},"invitedBy":{"anyOf":[{"$ref":"#/components/schemas/MemberRef"},{"type":"null"}]},"expiresAt":{"type":"string","format":"date-time"},"createdAt":{"type":"string","format":"date-time"}},"required":["id","email","role","status","invitedBy","expiresAt","createdAt"],"description":"An Owner's invitation for an email address to join with a role."},"CreateInvitationInput":{"type":"object","properties":{"email":{"type":"string","maxLength":254},"role":{"$ref":"#/components/schemas/WorkspaceRole"}},"required":["email","role"],"additionalProperties":false,"description":"Who to invite, with which role. Strict."},"InvitationSent":{"type":"object","properties":{"invitation":{"$ref":"#/components/schemas/Invitation"},"url":{"type":"string","format":"uri"},"emailSent":{"type":"boolean"}},"required":["invitation","url","emailSent"],"description":"A created or resent invitation, with its one-time link."},"AcceptInvitationInput":{"type":"object","properties":{"token":{"type":"string","minLength":16,"maxLength":200}},"required":["token"],"additionalProperties":false,"description":"The token from the invitation link. Strict."},"MagicLinkRequestInput":{"type":"object","properties":{"email":{"type":"string","maxLength":254},"name":{"type":"string"},"intent":{"type":"string","enum":["sign-in","sign-up","reset"],"default":"sign-in"},"locale":{"type":"string","enum":["en","pt-BR"],"default":"en"}},"required":["email"],"additionalProperties":false,"description":"Who to email a magic link to, and why. Strict."},"MagicLinkTokenInput":{"type":"object","properties":{"token":{"type":"string","minLength":16,"maxLength":200}},"required":["token"],"additionalProperties":false,"description":"The token from a magic link. Strict."},"MagicLinkPeek":{"type":"object","properties":{"email":{"type":"string"},"intent":{"type":"string","enum":["sign-in","sign-up","reset","verify","restore"]},"newAccount":{"type":"boolean"},"removesSignInMethods":{"type":"boolean"},"deletionPurgeAt":{"type":["string","null"],"format":"date-time"}},"required":["email","intent","newAccount","removesSignInMethods","deletionPurgeAt"],"description":"What a magic link does, before the click that spends it."},"MagicLinkVerified":{"type":"object","properties":{"intent":{"type":"string","enum":["sign-in","sign-up","reset","verify","restore"]},"newAccount":{"type":"boolean"},"passwordRemoved":{"type":"boolean"},"joined":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"}},"required":["id","name"]}}},"required":["intent","newAccount","passwordRemoved","joined"],"description":"The result of signing in with a magic link."},"PasskeyInfo":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":["string","null"]},"provider":{"type":["string","null"]},"backedUp":{"type":"boolean"},"createdAt":{"type":["string","null"]}},"required":["id","name","provider","backedUp","createdAt"],"description":"A passkey as listed: never its key."},"SignInMethods":{"type":"object","properties":{"email":{"type":"string"},"emailVerified":{"type":"boolean"},"password":{"type":"boolean"},"passkeys":{"type":"array","items":{"$ref":"#/components/schemas/PasskeyInfo"}},"freshSession":{"type":"boolean"}},"required":["email","emailVerified","password","passkeys","freshSession"],"description":"How the human signs in."},"SessionInfo":{"type":"object","properties":{"id":{"type":"string"},"device":{"type":"string"},"createdAt":{"type":"string"},"lastSeenAt":{"type":"string"},"ip":{"type":["string","null"]},"current":{"type":"boolean"}},"required":["id","device","createdAt","lastSeenAt","ip","current"],"description":"A signed-in browser or device. Never its token."},"SetPasswordInput":{"type":"object","properties":{"newPassword":{"type":"string","minLength":10,"maxLength":128},"currentPassword":{"type":"string","maxLength":128}},"required":["newPassword"],"additionalProperties":false,"description":"A new password (and the current one, unless fresh). Strict."},"Error":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","enum":["bad_request","approving_default","unknown_field","unauthorized","forbidden","workspace_suspended","email_not_verified","not_found","method_not_allowed","conflict","idempotency_key_in_use","idempotency_key_reused","rate_limited","internal_error","authorization_pending","slow_down","expired_token","access_denied","plan_limit","ceiling_reached","overloaded","service_unavailable"]},"message":{"type":"string","description":"For people; includes the field problems of a 400."},"hint":{"type":"string","description":"What to do next."},"docUrl":{"type":"string","description":"https://logu.dev/docs/errors#<code>"},"requestId":{"type":"string","description":"The request's id (also the `Logu-Request-Id` header): quote it when reporting a problem."},"issues":{"type":"array","items":{"type":"object","properties":{"path":{"type":"string"},"message":{"type":"string"}},"required":["path","message"]},"description":"Each invalid field (400s); `path` is dotted (`options.0.label`)."},"ask":{"$ref":"#/components/schemas/Ask"}},"required":["code","message","hint","docUrl","requestId"]}},"required":["error"],"description":"Every error has this shape. Branch on `code`; `hint` says what to do next. `ask` comes with a 409 `conflict` about an ask that is no longer waiting: the ask as it is now."}},"parameters":{}},"paths":{"/v1/events":{"post":{"operationId":"sendEvent","tags":["Events"],"summary":"Send an event","description":"Something an agent reports that needs no action (\"Deploy finished\"). Shown in the inbox and delivered like an ask, without options; muted projects and quiet hours hold its delivery. With a test API key (`lg_test_…`) it is `test: true`: shown in the app, never delivered.","security":[{"apiKey":[]}],"x-agent-hint":"Fire-and-forget: returns at once (201). Use for outcomes and progress the human should know about; use an ask instead when you need a decision. Send Idempotency-Key when retrying. Limit: 600 events and metrics per minute per API key.","x-logu-access":{"human":null,"agent":true},"parameters":[{"schema":{"type":"string","minLength":1,"maxLength":200,"example":"deploy-4821"},"required":false,"description":"Send a unique value per operation to retry safely: a retry with the same key and body returns the first result (`Idempotent-Replayed: true`). Per agent, remembered 24 hours.","name":"Idempotency-Key","in":"header"}],"requestBody":{"required":true,"content":{"application/json":{"example":{"project":"shop","title":"Deploy finished","body":"v1.4 is live.","icon":"🚀","url":"https://shop.example.com"},"schema":{"$ref":"#/components/schemas/EventInput"}}}},"responses":{"201":{"description":"The event. A replayed idempotent retry answers 200 with the original.","headers":{"Logu-Request-Id":{"$ref":"#/components/headers/RequestId"},"Idempotent-Replayed":{"$ref":"#/components/headers/IdempotentReplayed"},"RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"RateLimit-Policy":{"$ref":"#/components/headers/RateLimitPolicy"}},"content":{"application/json":{"example":{"id":"evt_01K7DQ2Z9X4M6P8R1T3V5W7Y9A","project":"shop","channel":null,"title":"Deploy finished","body":"v1.4 is live.","icon":"🚀","url":"https://shop.example.com","metadata":null,"agent":{"id":"agt_01K7D8F2H4K6M8P0R2T4V6X8Z0","name":"deploy-bot"},"attachments":[],"test":false,"createdAt":"2026-10-12T14:03:11.000Z"},"schema":{"$ref":"#/components/schemas/Event"}}}},"400":{"$ref":"#/components/responses/BadRequestOrUnknownField"},"401":{"$ref":"#/components/responses/Unauthorized"},"402":{"$ref":"#/components/responses/PlanLimitOrCeilingReached"},"403":{"$ref":"#/components/responses/ForbiddenOrWorkspaceSuspended"},"409":{"$ref":"#/components/responses/IdempotencyKeyInUse"},"422":{"$ref":"#/components/responses/IdempotencyKeyReused"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}},"get":{"operationId":"listEvents","tags":["Events"],"summary":"List events","description":"The workspace's events, newest first (events created in the same millisecond keep one order). Page with `cursor`: pass the previous page's `nextCursor` until it is null. An invalid cursor is a 400 (`bad_request`).","security":[{"apiKey":[]},{"session":[]}],"x-agent-hint":"Read-only; returns immediately. For more, repeat with ?cursor=<nextCursor> while nextCursor isn't null.","x-logu-access":{"human":"viewer","agent":true},"parameters":[{"schema":{"type":"integer","minimum":1,"maximum":100,"default":50,"example":50},"required":false,"description":"How many to return (1–100).","name":"limit","in":"query"},{"schema":{"type":"string","maxLength":200,"example":"ZXZlbnRzOjE3OTE4MTM3OTEwMDA6ZXZ0XzAxSzdEUTJaOVg0TTZQOFIxVDNWNVc3WTlB"},"required":false,"description":"Where this page starts: the `nextCursor` of the previous page, unchanged. Leave it out (or empty) for the first page.","name":"cursor","in":"query"},{"schema":{"type":"string","example":"wsp_01K7D7Y8Z0B2D4F6H8K0M2P4R6"},"required":false,"description":"Humans only: the workspace to act in (default: your personal workspace). Agents always act in their API key's workspace.","name":"Logu-Workspace","in":"header"}],"responses":{"200":{"description":"Events, newest first, and where the next page starts (null at the end).","headers":{"Logu-Request-Id":{"$ref":"#/components/headers/RequestId"},"RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"RateLimit-Policy":{"$ref":"#/components/headers/RateLimitPolicy"}},"content":{"application/json":{"example":{"events":[{"id":"evt_01K7DQ2Z9X4M6P8R1T3V5W7Y9A","project":"shop","channel":null,"title":"Deploy finished","body":"v1.4 is live.","icon":"🚀","url":"https://shop.example.com","metadata":null,"agent":{"id":"agt_01K7D8F2H4K6M8P0R2T4V6X8Z0","name":"deploy-bot"},"attachments":[],"test":false,"createdAt":"2026-10-12T14:03:11.000Z"}],"nextCursor":"ZXZlbnRzOjE3OTE4MTM3OTEwMDA6ZXZ0XzAxSzdEUTJaOVg0TTZQOFIxVDNWNVc3WTlB"},"schema":{"type":"object","properties":{"events":{"type":"array","items":{"$ref":"#/components/schemas/Event"}},"nextCursor":{"type":["string","null"],"description":"Pass as `cursor` for the next page, unchanged; null at the end of the list."}},"required":["events","nextCursor"]}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/ForbiddenOrWorkspaceSuspended"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/v1/attachments":{"post":{"operationId":"uploadAttachment","tags":["Attachments"],"summary":"Upload an image","description":"Upload an image to attach to an ask or event: send the file itself as the body with its `Content-Type` (and `?name=` for its file name), or as `multipart/form-data` in a `file` field. PNG, JPEG, WebP or GIF, recognized by the file's first bytes (not the declared type), up to 5 MB. Then list its id in `attachments` on `POST /v1/asks` or `POST /v1/events` (up to 4). Images are private to the workspace; an upload no ask or event uses within 24 hours is deleted.","security":[{"apiKey":[]}],"x-agent-hint":"Upload first, then create the ask or event with attachments: [\"att_…\"] (up to 4, each used once). Good for a UI change's before/after screenshots. curl: --data-binary @after.png -H \"Content-Type: image/png\" \"…/v1/attachments?name=after.png\". Limit: 120 images per minute per API key.","x-logu-access":{"human":null,"agent":true},"parameters":[{"schema":{"type":"string","maxLength":200,"example":"after.png"},"required":false,"description":"The file name (the image's alt text in the app).","name":"name","in":"query"}],"requestBody":{"required":true,"description":"The image's bytes, or multipart/form-data with the image in `file`.","content":{"image/png":{"schema":{"type":"string","format":"binary"}},"image/jpeg":{"schema":{"type":"string","format":"binary"}},"image/webp":{"schema":{"type":"string","format":"binary"}},"image/gif":{"schema":{"type":"string","format":"binary"}},"application/octet-stream":{"schema":{"type":"string","format":"binary"}},"multipart/form-data":{"schema":{"type":"object","properties":{"file":{"type":"string","format":"binary"}},"required":["file"]}}}},"responses":{"201":{"description":"The stored image, with a signed `url` and when it's deleted if nothing uses it (`expiresAt`).","headers":{"Logu-Request-Id":{"$ref":"#/components/headers/RequestId"},"RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"RateLimit-Policy":{"$ref":"#/components/headers/RateLimitPolicy"}},"content":{"application/json":{"example":{"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","expiresAt":"2026-10-13T14:01:52.000Z"},"schema":{"$ref":"#/components/schemas/UploadedAttachment"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"402":{"$ref":"#/components/responses/PlanLimitOrCeilingReached"},"403":{"$ref":"#/components/responses/ForbiddenOrWorkspaceSuspended"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/v1/attachments/{id}":{"get":{"operationId":"getAttachment","tags":["Attachments"],"summary":"Download an image","description":"An attachment's image, for members of its workspace and the agent that uploaded it. Asks and events also carry a signed `url` for each attachment that works without credentials for at least an hour (`<img>` tags, notifications); this endpoint is for when that has expired.","security":[{"apiKey":[]},{"session":[]}],"x-agent-hint":"Read-only; returns the image's bytes. Only the agent that uploaded it may read it.","x-logu-access":{"human":"viewer","agent":true},"parameters":[{"schema":{"type":"string","example":"att_01K7DQ6C8E0G2J4L6N8Q0S2U4W"},"required":true,"description":"The attachment's id (`att_…`).","name":"id","in":"path"},{"schema":{"type":"string","example":"wsp_01K7D7Y8Z0B2D4F6H8K0M2P4R6"},"required":false,"description":"Humans only: the workspace to act in (default: your personal workspace). Agents always act in their API key's workspace.","name":"Logu-Workspace","in":"header"}],"responses":{"200":{"description":"The image (inline, never sniffed).","headers":{"Logu-Request-Id":{"$ref":"#/components/headers/RequestId"},"RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"RateLimit-Policy":{"$ref":"#/components/headers/RateLimitPolicy"}},"content":{"image/png":{"schema":{"type":"string","format":"binary"}},"image/jpeg":{"schema":{"type":"string","format":"binary"}},"image/webp":{"schema":{"type":"string","format":"binary"}},"image/gif":{"schema":{"type":"string","format":"binary"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/ForbiddenOrWorkspaceSuspended"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/v1/asks":{"post":{"operationId":"createAsk","tags":["Asks"],"summary":"Ask a human for a decision","description":"An agent's request for a human decision: a title, an optional body (Markdown) and context link, options (default Approve / Decline, or free text with `allowText`), an expiry (`timeoutSeconds`) and an optional default option applied when it expires. The default can never be an approving option (the id `approve`, or any option with style `primary`): that is refused with `approving_default` and nothing is asked. Use `decline` to fail closed; pre-approval is an Owner's rule (`decidedVia: \"rule\"`). The workspace's Owners and Deciders get it by push, browser notifications, email, Telegram and Slack. With a test API key (`lg_test_…`) the ask is `test: true`: it is shown and decided in the app but never delivered, and its callback is still sent. Returns at once with the ask `waiting`; then wait for the decision with `GET /v1/asks/{id}/wait`. States: `waiting` → `decided` (a human chose, or the default option applied on expiry with `decision.decidedVia: \"timeout\"`), `timed_out` (expired, no default) or `withdrawn`. A decision with `decidedVia: \"timeout\"` is never an approval: only a human or an Owner's rule (`decidedVia: \"rule\"`) can approve.","security":[{"apiKey":[]}],"x-agent-hint":"Call before any risky or irreversible action (deploys, deletes, payments, emails to customers). This does NOT block: it returns the waiting ask (201). Then loop on GET /v1/asks/{id}/wait?timeout=50 while status is \"waiting\". Proceed only if status is \"decided\" and decision.optionId is the approving option. Set defaultOption: \"decline\" to fail closed (an approving default is refused: 400 approving_default; and a decision with decidedVia \"timeout\" is never an approval, whatever its option). Send Idempotency-Key so a retry never asks twice. Limit: 60 asks per minute per API key. A 402 plan_limit (the plan is out of asks this month) or ceiling_reached (Logu's cost ceiling for the workspace) means nothing was asked, so do not proceed; tell your human.","x-logu-access":{"human":null,"agent":true},"parameters":[{"schema":{"type":"string","minLength":1,"maxLength":200,"example":"deploy-4821"},"required":false,"description":"Send a unique value per operation to retry safely: a retry with the same key and body returns the first result (`Idempotent-Replayed: true`). Per agent, remembered 24 hours.","name":"Idempotency-Key","in":"header"}],"requestBody":{"required":true,"content":{"application/json":{"example":{"project":"shop","title":"Deploy v1.4 to production?","body":"3 commits since v1.3. CI is green.","url":"https://github.com/acme/shop/compare/v1.3...v1.4","timeoutSeconds":3600,"defaultOption":"decline","attachments":["att_01K7DQ6C8E0G2J4L6N8Q0S2U4W"]},"schema":{"$ref":"#/components/schemas/AskInput"}}}},"responses":{"201":{"description":"The ask, `waiting`. A replayed idempotent retry answers 200 with the original ask and `Idempotent-Replayed: true`.","headers":{"Logu-Request-Id":{"$ref":"#/components/headers/RequestId"},"Idempotent-Replayed":{"$ref":"#/components/headers/IdempotentReplayed"},"RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"RateLimit-Policy":{"$ref":"#/components/headers/RateLimitPolicy"}},"content":{"application/json":{"example":{"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":"waiting","decision":null,"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},"schema":{"$ref":"#/components/schemas/Ask"}}}},"400":{"$ref":"#/components/responses/BadRequestOrUnknownFieldOrApprovingDefault"},"401":{"$ref":"#/components/responses/Unauthorized"},"402":{"$ref":"#/components/responses/PlanLimitOrCeilingReached"},"403":{"$ref":"#/components/responses/ForbiddenOrWorkspaceSuspended"},"409":{"$ref":"#/components/responses/IdempotencyKeyInUse"},"422":{"$ref":"#/components/responses/IdempotencyKeyReused"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"$ref":"#/components/responses/ServiceUnavailable"}}},"get":{"operationId":"listAsks","tags":["Asks"],"summary":"List asks","description":"The workspace's asks, newest first (asks created in the same millisecond keep one order). Page with `cursor`: pass the previous page's `nextCursor` until it is null. `before` (the last id of the previous page) still works. An invalid cursor is a 400 (`bad_request`).","security":[{"apiKey":[]},{"session":[]}],"x-agent-hint":"Use to recover asks you lost track of (e.g. after a restart): GET /v1/asks?status=waiting. Prefer GET /v1/asks/{id}/wait when you know the id. For more, repeat with ?cursor=<nextCursor> while nextCursor isn't null.","x-logu-access":{"human":"viewer","agent":true},"parameters":[{"schema":{"type":"integer","minimum":1,"maximum":100,"default":50,"example":50},"required":false,"description":"How many to return (1–100).","name":"limit","in":"query"},{"schema":{"type":"string","enum":["waiting","decided","timed_out","withdrawn"],"description":"`waiting` (not yet decided), `decided` (a human chose, or the default applied on expiry), `timed_out` (expired with no default) or `withdrawn` (the agent took it back).","example":"waiting"},"required":false,"description":"Only asks in this state (`waiting` for the ones still open).","name":"status","in":"query"},{"schema":{"type":"string","pattern":"^ask_[A-Za-z0-9]+$","example":"ask_01K7DQ3M2V8H4N6R9T1W3Y5B7C"},"required":false,"description":"The last ask id of the previous page (asks are newest first). Prefer `cursor`; not with it.","name":"before","in":"query"},{"schema":{"type":"string","maxLength":200,"example":"YXNrczoxNzkxODEzNzkxMDAwOmFza18wMUs3RFEzTTJWOEg0TjZSOVQxVzNZNUI3Qw"},"required":false,"description":"Where this page starts: the `nextCursor` of the previous page, unchanged. Leave it out (or empty) for the first page.","name":"cursor","in":"query"},{"schema":{"type":"string","example":"wsp_01K7D7Y8Z0B2D4F6H8K0M2P4R6"},"required":false,"description":"Humans only: the workspace to act in (default: your personal workspace). Agents always act in their API key's workspace.","name":"Logu-Workspace","in":"header"}],"responses":{"200":{"description":"Asks, newest first, and where the next page starts (null at the end).","headers":{"Logu-Request-Id":{"$ref":"#/components/headers/RequestId"},"RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"RateLimit-Policy":{"$ref":"#/components/headers/RateLimitPolicy"}},"content":{"application/json":{"example":{"asks":[{"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":"waiting","decision":null,"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}],"nextCursor":"YXNrczoxNzkxODEzNzkxMDAwOmFza18wMUs3RFEzTTJWOEg0TjZSOVQxVzNZNUI3Qw"},"schema":{"type":"object","properties":{"asks":{"type":"array","items":{"$ref":"#/components/schemas/Ask"}},"nextCursor":{"type":["string","null"],"description":"Pass as `cursor` for the next page, unchanged; null at the end of the list."}},"required":["asks","nextCursor"]}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/ForbiddenOrWorkspaceSuspended"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/v1/asks/stats":{"get":{"operationId":"getDecisionSpeed","tags":["Asks"],"summary":"Decision speed over a range","description":"How quickly the workspace's members decided over `range` (24h, 7d, 30d, 90d or 1y, ending now): asks decided by a human, asks that ran out of time (with or without a default option), and the median and 90th percentile time from ask to a human decision. Live asks only (test asks aren't counted). Settled asks are kept 180 days, so 1y covers at most that.","security":[{"session":[]}],"x-logu-access":{"human":"viewer","agent":false},"parameters":[{"schema":{"type":"string","enum":["24h","7d","30d","90d","1y"],"default":"7d","example":"30d"},"required":false,"description":"The window, ending now: `24h`, `7d`, `30d`, `90d`, `1y`.","name":"range","in":"query"},{"schema":{"type":"string","example":"wsp_01K7D7Y8Z0B2D4F6H8K0M2P4R6"},"required":false,"description":"Humans only: the workspace to act in (default: your personal workspace). Agents always act in their API key's workspace.","name":"Logu-Workspace","in":"header"}],"responses":{"200":{"description":"Decision speed.","headers":{"Logu-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"example":{"range":"30d","from":"2026-09-13T14:03:11.000Z","to":"2026-10-13T14:03:11.000Z","decided":48,"timedOut":3,"medianDecisionSeconds":151,"p90DecisionSeconds":1260},"schema":{"type":"object","properties":{"range":{"type":"string","enum":["24h","7d","30d","90d","1y"]},"from":{"type":"string","format":"date-time"},"to":{"type":"string","format":"date-time"},"decided":{"type":"integer"},"timedOut":{"type":"integer"},"medianDecisionSeconds":{"type":["number","null"]},"p90DecisionSeconds":{"type":["number","null"]}},"required":["range","from","to","decided","timedOut","medianDecisionSeconds","p90DecisionSeconds"]}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/v1/asks/{id}":{"get":{"operationId":"getAsk","tags":["Asks"],"summary":"Get an ask","description":"One ask as it is now, with its decision once decided. States: `waiting` → `decided` (a human chose, or the default option applied on expiry with `decision.decidedVia: \"timeout\"`), `timed_out` (expired, no default) or `withdrawn`. A decision with `decidedVia: \"timeout\"` is never an approval: only a human or an Owner's rule (`decidedVia: \"rule\"`) can approve.","security":[{"apiKey":[]},{"session":[]}],"x-agent-hint":"Returns immediately. To wait for the decision, use GET /v1/asks/{id}/wait instead of polling this.","x-logu-access":{"human":"viewer","agent":true},"parameters":[{"schema":{"type":"string","example":"ask_01K7DQ3M2V8H4N6R9T1W3Y5B7C"},"required":true,"description":"The ask's id (`ask_…`).","name":"id","in":"path"},{"schema":{"type":"string","example":"wsp_01K7D7Y8Z0B2D4F6H8K0M2P4R6"},"required":false,"description":"Humans only: the workspace to act in (default: your personal workspace). Agents always act in their API key's workspace.","name":"Logu-Workspace","in":"header"}],"responses":{"200":{"description":"The ask.","headers":{"Logu-Request-Id":{"$ref":"#/components/headers/RequestId"},"RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"RateLimit-Policy":{"$ref":"#/components/headers/RateLimitPolicy"}},"content":{"application/json":{"examples":{"waiting":{"summary":"Still waiting","value":{"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":"waiting","decision":null,"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}},"decided":{"summary":"Decided by Ana","value":{"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}}},"schema":{"$ref":"#/components/schemas/Ask"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/ForbiddenOrWorkspaceSuspended"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/v1/asks/{id}/history":{"get":{"operationId":"getAskHistory","tags":["Asks"],"summary":"How this ask was decided before","description":"For the app's \"You've approved this 7 times\" hint: how members decided earlier asks from the same agent, in the same project, with exactly the same title, per option (`count`: decisions by a human; `automated`: the same option chosen by a rule or by the default on timeout; `lastAt`: when a human last chose it). Most chosen first. Test asks and the ask itself aren't counted; settled asks are kept 180 days. Empty when the ask has no agent or nobody decided one like it. Members only: agents don't see how their asks were decided this way.","security":[{"session":[]}],"x-logu-access":{"human":"viewer","agent":false},"parameters":[{"schema":{"type":"string","example":"ask_01K7DQ3M2V8H4N6R9T1W3Y5B7C"},"required":true,"description":"The ask's id (`ask_…`).","name":"id","in":"path"},{"schema":{"type":"string","example":"wsp_01K7D7Y8Z0B2D4F6H8K0M2P4R6"},"required":false,"description":"Humans only: the workspace to act in (default: your personal workspace). Agents always act in their API key's workspace.","name":"Logu-Workspace","in":"header"}],"responses":{"200":{"description":"Past decisions, per option.","headers":{"Logu-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"example":{"history":[{"optionId":"approve","label":"Approve","count":7,"automated":2,"lastAt":"2026-10-12T14:05:42.000Z"},{"optionId":"decline","label":"Decline","count":1,"automated":0,"lastAt":"2026-10-12T14:03:11.000Z"}]},"schema":{"type":"object","properties":{"history":{"type":"array","items":{"type":"object","properties":{"optionId":{"type":"string"},"label":{"type":"string"},"count":{"type":"integer","minimum":1},"automated":{"type":"integer","minimum":0},"lastAt":{"type":"string","format":"date-time"}},"required":["optionId","label","count","automated","lastAt"]}}},"required":["history"]}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/v1/asks/{id}/wait":{"get":{"operationId":"waitForAsk","tags":["Asks"],"summary":"Wait for the decision (long-poll)","description":"Holds the request until the ask settles, or for `timeout` seconds (at most 50), then returns the ask either way. Waiting never changes the ask's expiry. A workspace holds at most 150 waits open at once (5 per ask): over that, the answer is immediate, `waiting`, with a `Retry-After` header. A hub that can't take the wait answers 503 `overloaded` with `Retry-After`; nothing is lost.","security":[{"apiKey":[]},{"session":[]}],"x-agent-hint":"BLOCKS up to `timeout` seconds (max 50). Call in a loop while the returned status is \"waiting\"; set your HTTP client's timeout above the wait. A 200 with status \"waiting\" is normal, not an error: if it came back sooner than the timeout, wait for the Retry-After header (at least 1 s) before calling again. On 503 overloaded, wait Retry-After and retry. Stop when status is decided, timed_out or withdrawn. If you give up waiting, withdraw the ask (POST /v1/asks/{id}/withdraw).","x-logu-access":{"human":"viewer","agent":true},"parameters":[{"schema":{"type":"string","example":"ask_01K7DQ3M2V8H4N6R9T1W3Y5B7C"},"required":true,"description":"The ask's id (`ask_…`).","name":"id","in":"path"},{"schema":{"type":"integer","minimum":1,"maximum":50,"default":25,"example":50},"required":false,"description":"Seconds to hold the request while the ask is waiting (1–50).","name":"timeout","in":"query"},{"schema":{"type":"string","example":"wsp_01K7D7Y8Z0B2D4F6H8K0M2P4R6"},"required":false,"description":"Humans only: the workspace to act in (default: your personal workspace). Agents always act in their API key's workspace.","name":"Logu-Workspace","in":"header"}],"responses":{"200":{"description":"The ask: settled, or still `waiting` when the wait ran out.","headers":{"Logu-Request-Id":{"$ref":"#/components/headers/RequestId"},"RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"RateLimit-Policy":{"$ref":"#/components/headers/RateLimitPolicy"}},"content":{"application/json":{"examples":{"decided":{"summary":"Decided while waiting","value":{"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}},"waiting":{"summary":"Wait ran out; call again","value":{"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":"waiting","decision":null,"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}}},"schema":{"$ref":"#/components/schemas/Ask"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/ForbiddenOrWorkspaceSuspended"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"$ref":"#/components/responses/Overloaded"}}}},"/v1/asks/{id}/withdraw":{"post":{"operationId":"withdrawAsk","tags":["Asks"],"summary":"Withdraw an ask","description":"Takes back an ask nobody decided yet (the agent no longer needs it, or stopped waiting). An agent can withdraw only its own asks; a Decider or Owner may withdraw any. The ask ends `withdrawn`.","security":[{"apiKey":[]},{"session":[]}],"x-agent-hint":"Call when you stop waiting or no longer need the decision, so the human isn't asked for nothing. A 409 conflict means it already settled: read error.ask instead of retrying.","x-logu-access":{"human":"decider","agent":true},"parameters":[{"schema":{"type":"string","example":"ask_01K7DQ3M2V8H4N6R9T1W3Y5B7C"},"required":true,"description":"The ask's id (`ask_…`).","name":"id","in":"path"},{"schema":{"type":"string","example":"wsp_01K7D7Y8Z0B2D4F6H8K0M2P4R6"},"required":false,"description":"Humans only: the workspace to act in (default: your personal workspace). Agents always act in their API key's workspace.","name":"Logu-Workspace","in":"header"}],"responses":{"200":{"description":"The ask, `withdrawn`.","headers":{"Logu-Request-Id":{"$ref":"#/components/headers/RequestId"},"RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"RateLimit-Policy":{"$ref":"#/components/headers/RateLimitPolicy"}},"content":{"application/json":{"example":{"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":"withdrawn","decision":null,"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},"schema":{"$ref":"#/components/schemas/Ask"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/ForbiddenOrWorkspaceSuspended"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/v1/asks/{id}/decision":{"post":{"operationId":"decideAsk","tags":["Asks"],"summary":"Decide an ask (humans only)","description":"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.","security":[{"session":[]}],"x-logu-access":{"human":"decider","agent":false},"parameters":[{"schema":{"type":"string","example":"ask_01K7DQ3M2V8H4N6R9T1W3Y5B7C"},"required":true,"description":"The ask's id (`ask_…`).","name":"id","in":"path"},{"schema":{"type":"string","example":"wsp_01K7D7Y8Z0B2D4F6H8K0M2P4R6"},"required":false,"description":"Humans only: the workspace to act in (default: your personal workspace). Agents always act in their API key's workspace.","name":"Logu-Workspace","in":"header"}],"requestBody":{"required":true,"content":{"application/json":{"example":{"optionId":"approve"},"schema":{"$ref":"#/components/schemas/DecisionInput"}}}},"responses":{"200":{"description":"The ask, `decided`, with `decision.decidedBy`.","headers":{"Logu-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"example":{"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},"schema":{"$ref":"#/components/schemas/Ask"}}}},"400":{"$ref":"#/components/responses/BadRequestOrUnknownField"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/v1/events/{id}":{"get":{"operationId":"getEvent","tags":["Events"],"summary":"Get an event","description":"One event of the workspace. The app opens an event's link (`https://logu.dev/events/{id}`) with it, even when the event is no longer in the latest list.","security":[{"apiKey":[]},{"session":[]}],"x-agent-hint":"Read-only; returns immediately.","x-logu-access":{"human":"viewer","agent":true},"parameters":[{"schema":{"type":"string","example":"ask_01K7DQ3M2V8H4N6R9T1W3Y5B7C"},"required":true,"description":"The resource's id.","name":"id","in":"path"},{"schema":{"type":"string","example":"wsp_01K7D7Y8Z0B2D4F6H8K0M2P4R6"},"required":false,"description":"Humans only: the workspace to act in (default: your personal workspace). Agents always act in their API key's workspace.","name":"Logu-Workspace","in":"header"}],"responses":{"200":{"description":"The event.","headers":{"Logu-Request-Id":{"$ref":"#/components/headers/RequestId"},"RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"RateLimit-Policy":{"$ref":"#/components/headers/RateLimitPolicy"}},"content":{"application/json":{"example":{"id":"evt_01K7DQ2Z9X4M6P8R1T3V5W7Y9A","project":"shop","channel":null,"title":"Deploy finished","body":"v1.4 is live.","icon":"🚀","url":"https://shop.example.com","metadata":null,"agent":{"id":"agt_01K7D8F2H4K6M8P0R2T4V6X8Z0","name":"deploy-bot"},"attachments":[],"test":false,"createdAt":"2026-10-12T14:03:11.000Z"},"schema":{"$ref":"#/components/schemas/Event"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/ForbiddenOrWorkspaceSuspended"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/v1/metrics":{"post":{"operationId":"reportMetric","tags":["Metrics"],"summary":"Report a metric value","description":"A named number reported over time (`orders = 42`). The app's Insights section shows it with a trend; threshold alerts send an event when it crosses a value. Deciders can report too. With a test API key (`lg_test_…`) it goes to a test metric of that name (`test: true`), apart from the live one and out of the long-term history; its threshold alerts send test events.","security":[{"apiKey":[]},{"session":[]}],"x-agent-hint":"Fire-and-forget: returns at once (201). Report the current value each time it changes or on a schedule; the metric is created on first report. Limit: 600 events and metrics per minute per API key.","x-logu-access":{"human":"decider","agent":true},"parameters":[{"schema":{"type":"string","example":"wsp_01K7D7Y8Z0B2D4F6H8K0M2P4R6"},"required":false,"description":"Humans only: the workspace to act in (default: your personal workspace). Agents always act in their API key's workspace.","name":"Logu-Workspace","in":"header"},{"schema":{"type":"string","minLength":1,"maxLength":200,"example":"deploy-4821"},"required":false,"description":"Send a unique value per operation to retry safely: a retry with the same key and body returns the first result (`Idempotent-Replayed: true`). Per agent, remembered 24 hours.","name":"Idempotency-Key","in":"header"}],"requestBody":{"required":true,"content":{"application/json":{"example":{"project":"shop","metric":"orders","value":42,"unit":"orders","label":"Orders today"},"schema":{"$ref":"#/components/schemas/MetricInput"}}}},"responses":{"201":{"description":"The metric with its latest value.","headers":{"Logu-Request-Id":{"$ref":"#/components/headers/RequestId"},"Idempotent-Replayed":{"$ref":"#/components/headers/IdempotentReplayed"},"RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"RateLimit-Policy":{"$ref":"#/components/headers/RateLimitPolicy"}},"content":{"application/json":{"example":{"id":"met_01K7DQ4A6C8E0G2J4L6N8Q0S2U","project":"shop","metric":"orders","label":"Orders today","unit":"orders","value":42,"at":"2026-10-12T14:03:11.000Z","agent":{"id":"agt_01K7D8F2H4K6M8P0R2T4V6X8Z0","name":"deploy-bot"},"test":false},"schema":{"$ref":"#/components/schemas/RecordedMetric"}}}},"400":{"$ref":"#/components/responses/BadRequestOrUnknownField"},"401":{"$ref":"#/components/responses/Unauthorized"},"402":{"$ref":"#/components/responses/PlanLimitOrCeilingReached"},"403":{"$ref":"#/components/responses/ForbiddenOrWorkspaceSuspended"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/IdempotencyKeyInUse"},"422":{"$ref":"#/components/responses/IdempotencyKeyReused"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}},"get":{"operationId":"getInsights","tags":["Metrics"],"summary":"Insights: metrics and ask stats","description":"What the app's Insights section shows: every metric with its recent points and threshold alerts (live metrics first, then test ones), and how quickly asks are decided (live asks only).","security":[{"session":[]}],"x-logu-access":{"human":"viewer","agent":false},"parameters":[{"schema":{"type":"string","example":"wsp_01K7D7Y8Z0B2D4F6H8K0M2P4R6"},"required":false,"description":"Humans only: the workspace to act in (default: your personal workspace). Agents always act in their API key's workspace.","name":"Logu-Workspace","in":"header"}],"responses":{"200":{"description":"Metrics and ask stats.","headers":{"Logu-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"example":{"metrics":[{"id":"met_01K7DQ4A6C8E0G2J4L6N8Q0S2U","project":"shop","metric":"orders","label":"Orders today","unit":"orders","value":42,"at":"2026-10-12T14:03:11.000Z","agent":{"id":"agt_01K7D8F2H4K6M8P0R2T4V6X8Z0","name":"deploy-bot"},"test":false,"points":[{"value":37,"at":"2026-10-12T13:03:11.000Z"},{"value":42,"at":"2026-10-12T14:03:11.000Z"}],"alerts":[{"id":"alr_01K7DQ5B7D9F1H3K5M7P9R1T3V","condition":"below","threshold":10,"triggered":false}]}],"asks":{"waiting":1,"decided7d":12,"timedOut7d":1,"medianDecisionSeconds":151}},"schema":{"$ref":"#/components/schemas/Insights"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/v1/metrics/{id}/history":{"get":{"operationId":"getMetricHistory","tags":["Metrics"],"summary":"A metric's history over a range","description":"The metric over `range` (24h, 7d, 30d, 90d or 1y, ending now), grouped in buckets (15 minutes for 24h, 1 hour for 7d, 6 hours for 30d, 1 day for 90d, 1 week for 1y): each bucket's average, min, max and number of reports, and the range's min, max, average and change from the first bucket to the last; `previous` summarizes the period of the same length just before it (null when that has no reports), to compare with. `source: \"analytics\"` is the long-term history (every live report, kept by Analytics Engine for about three months); `source: \"recent\"` means only the last reports kept in Logu, when the server has no long-term history set up or couldn't reach it, and always for test metrics. Answers are reused for up to a minute.","security":[{"session":[]}],"x-logu-access":{"human":"viewer","agent":false},"parameters":[{"schema":{"type":"string","example":"met_01K7DQ4A6C8E0G2J4L6N8Q0S2U"},"required":true,"description":"The metric's id (`met_…`).","name":"id","in":"path"},{"schema":{"type":"string","enum":["24h","7d","30d","90d","1y"],"default":"7d","example":"30d"},"required":false,"description":"The window, ending now: `24h`, `7d`, `30d`, `90d`, `1y`.","name":"range","in":"query"},{"schema":{"type":"string","example":"wsp_01K7D7Y8Z0B2D4F6H8K0M2P4R6"},"required":false,"description":"Humans only: the workspace to act in (default: your personal workspace). Agents always act in their API key's workspace.","name":"Logu-Workspace","in":"header"}],"responses":{"200":{"description":"The metric's history.","headers":{"Logu-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"example":{"metricId":"met_01K7DQ4A6C8E0G2J4L6N8Q0S2U","range":"7d","from":"2026-10-06T14:00:00.000Z","to":"2026-10-13T14:00:00.000Z","bucketSeconds":3600,"source":"analytics","buckets":[{"at":"2026-10-12T13:00:00.000Z","value":37,"min":35,"max":39,"count":4},{"at":"2026-10-13T13:00:00.000Z","value":41.5,"min":41,"max":42,"count":2}],"summary":{"min":35,"max":42,"avg":38.5,"first":37,"last":41.5,"change":12.16,"count":6},"previous":{"min":30,"max":38,"avg":34,"first":31,"last":36,"change":16.13,"count":9}},"schema":{"type":"object","properties":{"metricId":{"type":"string"},"range":{"type":"string","enum":["24h","7d","30d","90d","1y"]},"from":{"type":"string","format":"date-time"},"to":{"type":"string","format":"date-time"},"bucketSeconds":{"type":"integer"},"source":{"type":"string","enum":["analytics","recent"]},"buckets":{"type":"array","items":{"type":"object","properties":{"at":{"type":"string","format":"date-time"},"value":{"type":"number"},"min":{"type":"number"},"max":{"type":"number"},"count":{"type":"number"}},"required":["at","value","min","max","count"]}},"summary":{"type":["object","null"],"properties":{"min":{"type":"number"},"max":{"type":"number"},"avg":{"type":"number"},"first":{"type":"number"},"last":{"type":"number"},"change":{"type":["number","null"]},"count":{"type":"number"}},"required":["min","max","avg","first","last","change","count"]},"previous":{"type":["object","null"],"properties":{"min":{"type":"number"},"max":{"type":"number"},"avg":{"type":"number"},"first":{"type":"number"},"last":{"type":"number"},"change":{"type":["number","null"]},"count":{"type":"number"}},"required":["min","max","avg","first","last","change","count"]}},"required":["metricId","range","from","to","bucketSeconds","source","buckets","summary","previous"]}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/v1/metrics/{id}":{"delete":{"operationId":"deleteMetric","tags":["Metrics"],"summary":"Delete a metric","description":"Deletes the metric, its points and its threshold alerts.","security":[{"session":[]}],"x-logu-access":{"human":"decider","agent":false},"parameters":[{"schema":{"type":"string","example":"met_01K7DQ4A6C8E0G2J4L6N8Q0S2U"},"required":true,"description":"The metric's id (`met_…`).","name":"id","in":"path"},{"schema":{"type":"string","example":"wsp_01K7D7Y8Z0B2D4F6H8K0M2P4R6"},"required":false,"description":"Humans only: the workspace to act in (default: your personal workspace). Agents always act in their API key's workspace.","name":"Logu-Workspace","in":"header"}],"responses":{"204":{"description":"Deleted.","headers":{"Logu-Request-Id":{"$ref":"#/components/headers/RequestId"}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/v1/metrics/{id}/alerts":{"post":{"operationId":"createAlert","tags":["Metrics"],"summary":"Add a threshold alert","description":"Tell the members when the metric goes above or below a value: an event once per crossing (again only after it recovers). Up to 10 per metric.","security":[{"session":[]}],"x-logu-access":{"human":"decider","agent":false},"parameters":[{"schema":{"type":"string","example":"met_01K7DQ4A6C8E0G2J4L6N8Q0S2U"},"required":true,"description":"The metric's id (`met_…`).","name":"id","in":"path"},{"schema":{"type":"string","example":"wsp_01K7D7Y8Z0B2D4F6H8K0M2P4R6"},"required":false,"description":"Humans only: the workspace to act in (default: your personal workspace). Agents always act in their API key's workspace.","name":"Logu-Workspace","in":"header"}],"requestBody":{"required":true,"content":{"application/json":{"example":{"condition":"below","threshold":10},"schema":{"$ref":"#/components/schemas/CreateMetricAlertInput"}}}},"responses":{"201":{"description":"The threshold alert.","headers":{"Logu-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"example":{"id":"alr_01K7DQ5B7D9F1H3K5M7P9R1T3V","condition":"below","threshold":10,"triggered":false},"schema":{"$ref":"#/components/schemas/MetricAlert"}}}},"400":{"$ref":"#/components/responses/BadRequestOrUnknownField"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/v1/metrics/alerts/{alertId}":{"delete":{"operationId":"deleteAlert","tags":["Metrics"],"summary":"Delete a threshold alert","description":"Removes the threshold alert.","security":[{"session":[]}],"x-logu-access":{"human":"decider","agent":false},"parameters":[{"schema":{"type":"string","example":"alr_01K7DQ5B7D9F1H3K5M7P9R1T3V"},"required":true,"description":"The threshold alert's id (`alr_…`).","name":"alertId","in":"path"},{"schema":{"type":"string","example":"wsp_01K7D7Y8Z0B2D4F6H8K0M2P4R6"},"required":false,"description":"Humans only: the workspace to act in (default: your personal workspace). Agents always act in their API key's workspace.","name":"Logu-Workspace","in":"header"}],"responses":{"204":{"description":"Deleted.","headers":{"Logu-Request-Id":{"$ref":"#/components/headers/RequestId"}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/v1/projects":{"get":{"operationId":"listProjects","tags":["Delivery"],"summary":"List projects","description":"The workspace's projects (created the first time an agent sends to one). `muted` is the signed-in member's own mute (always false for agents).","security":[{"apiKey":[]},{"session":[]}],"x-agent-hint":"Use to reuse existing project names instead of inventing new ones.","x-logu-access":{"human":"viewer","agent":true},"parameters":[{"schema":{"type":"string","example":"wsp_01K7D7Y8Z0B2D4F6H8K0M2P4R6"},"required":false,"description":"Humans only: the workspace to act in (default: your personal workspace). Agents always act in their API key's workspace.","name":"Logu-Workspace","in":"header"}],"responses":{"200":{"description":"Projects by name.","headers":{"Logu-Request-Id":{"$ref":"#/components/headers/RequestId"},"RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"RateLimit-Policy":{"$ref":"#/components/headers/RateLimitPolicy"}},"content":{"application/json":{"example":{"projects":[{"id":"prj_01K7D9A1C3E5G7J9L1N3Q5S7U9","name":"shop","muted":false,"routing":{"notify":null,"escalation":null}}]},"schema":{"type":"object","properties":{"projects":{"type":"array","items":{"$ref":"#/components/schemas/Project"}}},"required":["projects"]}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/ForbiddenOrWorkspaceSuspended"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/v1/projects/{id}/routing":{"put":{"operationId":"setProjectRouting","tags":["Delivery"],"summary":"Set who is notified of a project's asks","description":"Owners only. Routing: `notify` lists the Owners and Deciders (user ids) notified of the project's asks; null notifies every Owner and Decider (the default). It only changes who is notified: every member still sees the asks in the app and decides them per role. When an ask is sent, the routed members who can't be reached (no delivery on, or holding asks in their quiet hours) are skipped, and if none is left it goes to every Owner and Decider. Escalation: if an ask is still waiting `afterMinutes` after it was sent, also notify all Owners (`to: \"owners\"`) or the listed Owners and Deciders (`to: \"members\"`), once; shown on the ask (`escalation`) and in the activity log. Both fields are required; null restores the default. Up to 50 members each.","security":[{"session":[]}],"x-logu-access":{"human":"owner","agent":false},"parameters":[{"schema":{"type":"string","example":"prj_01K7D9A1C3E5G7J9L1N3Q5S7U9"},"required":true,"description":"The project's id (`prj_…`).","name":"id","in":"path"},{"schema":{"type":"string","example":"wsp_01K7D7Y8Z0B2D4F6H8K0M2P4R6"},"required":false,"description":"Humans only: the workspace to act in (default: your personal workspace). Agents always act in their API key's workspace.","name":"Logu-Workspace","in":"header"}],"requestBody":{"required":true,"content":{"application/json":{"example":{"notify":["Hk9cW2vZpT4mQ6nJ"],"escalation":{"afterMinutes":30,"to":"owners"}},"schema":{"type":"object","properties":{"notify":{"type":["array","null"],"items":{"type":"string","minLength":1},"minItems":1,"maxItems":50},"escalation":{"oneOf":[{"type":"object","properties":{"afterMinutes":{"type":"integer","minimum":1,"maximum":43200},"to":{"type":"string","enum":["owners"]}},"required":["afterMinutes","to"],"additionalProperties":false},{"type":"object","properties":{"afterMinutes":{"type":"integer","minimum":1,"maximum":43200},"to":{"type":"string","enum":["members"]},"members":{"type":"array","items":{"type":"string","minLength":1},"minItems":1,"maxItems":50}},"required":["afterMinutes","to","members"],"additionalProperties":false},{"type":"null"}]}},"required":["notify","escalation"],"additionalProperties":false}}}},"responses":{"200":{"description":"The project with its routing.","headers":{"Logu-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"example":{"id":"prj_01K7D9A1C3E5G7J9L1N3Q5S7U9","name":"shop","muted":false,"routing":{"notify":["Hk9cW2vZpT4mQ6nJ"],"escalation":{"afterMinutes":30,"to":"owners"}}},"schema":{"$ref":"#/components/schemas/Project"}}}},"400":{"$ref":"#/components/responses/BadRequestOrUnknownField"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/v1/rules":{"get":{"operationId":"listRules","tags":["Rules"],"summary":"List rules","description":"The workspace's rules, newest first, each with its uses and `status` (`active`, `expired`, `used_up`, or `inactive` once its creator is no longer an Owner). Owners only.","security":[{"session":[]}],"x-logu-access":{"human":"owner","agent":false},"parameters":[{"schema":{"type":"string","example":"wsp_01K7D7Y8Z0B2D4F6H8K0M2P4R6"},"required":false,"description":"Humans only: the workspace to act in (default: your personal workspace). Agents always act in their API key's workspace.","name":"Logu-Workspace","in":"header"}],"responses":{"200":{"description":"Rules.","headers":{"Logu-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"example":{"rules":[{"id":"rul_01K7DC7G9J1L3N5Q7S9U1W3Y5A","projectId":"prj_01K7D9A1C3E5G7J9L1N3Q5S7U9","project":"shop","agent":{"id":"agt_01K7D8F2H4K6M8P0R2T4V6X8Z0","name":"deploy-bot"},"title":"Deploy v1.4 to staging?","optionId":"approve","expiresAt":"2026-11-12T14:00:00.000Z","maxUses":20,"uses":3,"lastUsedAt":"2026-10-12T14:05:42.000Z","status":"active","createdBy":{"id":"Xq3bY7tLmN2pR8sV","name":"Ana"},"createdAt":"2026-10-12T14:03:11.000Z"}]},"schema":{"type":"object","properties":{"rules":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"projectId":{"type":"string"},"project":{"type":"string"},"agent":{"$ref":"#/components/schemas/AgentRef"},"title":{"type":["string","null"]},"optionId":{"type":"string"},"expiresAt":{"type":["string","null"],"format":"date-time"},"maxUses":{"type":["integer","null"]},"uses":{"type":"integer"},"lastUsedAt":{"type":["string","null"],"format":"date-time"},"status":{"type":"string","enum":["active","expired","used_up","inactive"]},"createdBy":{"anyOf":[{"$ref":"#/components/schemas/MemberRef"},{"type":"null"}]},"createdAt":{"type":"string","format":"date-time"}},"required":["id","projectId","project","agent","title","optionId","expiresAt","maxUses","uses","lastUsedAt","status","createdBy","createdAt"]}}},"required":["rules"]}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"500":{"$ref":"#/components/responses/InternalError"}}},"post":{"operationId":"createRule","tags":["Rules"],"summary":"Create a rule","description":"An Owner's decision made in advance, narrow by design: asks from one agent (`agentId`) in one project (`projectId`), with exactly `title` when it's set, that offer `optionId` are decided with that option as soon as they're sent, by this Owner (`decidedVia: \"rule\"`, `decidedBy`: the Owner, `ruleId`). Optionally until `expiresAt` and for `maxUses` asks. Only asks sent after it's created; test asks too. A rule decides nothing once its creator is no longer an Owner. Owners only, with a session: API keys can never create rules. Up to 100 per workspace.","security":[{"session":[]}],"x-logu-access":{"human":"owner","agent":false},"parameters":[{"schema":{"type":"string","example":"wsp_01K7D7Y8Z0B2D4F6H8K0M2P4R6"},"required":false,"description":"Humans only: the workspace to act in (default: your personal workspace). Agents always act in their API key's workspace.","name":"Logu-Workspace","in":"header"}],"requestBody":{"required":true,"content":{"application/json":{"example":{"projectId":"prj_01K7D9A1C3E5G7J9L1N3Q5S7U9","agentId":"agt_01K7D8F2H4K6M8P0R2T4V6X8Z0","title":"Deploy v1.4 to staging?","optionId":"approve","expiresAt":"2026-11-12T14:00:00.000Z","maxUses":20},"schema":{"type":"object","properties":{"projectId":{"type":"string","minLength":1},"agentId":{"type":"string","minLength":1},"title":{"type":"string","minLength":1,"maxLength":200},"optionId":{"type":"string","minLength":1,"maxLength":64,"pattern":"^[a-z0-9][a-z0-9._-]*$"},"expiresAt":{"type":"string","format":"date-time"},"maxUses":{"type":"integer","minimum":1,"maximum":10000}},"required":["projectId","agentId","optionId"],"additionalProperties":false}}}},"responses":{"201":{"description":"The rule.","headers":{"Logu-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"example":{"id":"rul_01K7DC7G9J1L3N5Q7S9U1W3Y5A","projectId":"prj_01K7D9A1C3E5G7J9L1N3Q5S7U9","project":"shop","agent":{"id":"agt_01K7D8F2H4K6M8P0R2T4V6X8Z0","name":"deploy-bot"},"title":"Deploy v1.4 to staging?","optionId":"approve","expiresAt":"2026-11-12T14:00:00.000Z","maxUses":20,"uses":3,"lastUsedAt":"2026-10-12T14:05:42.000Z","status":"active","createdBy":{"id":"Xq3bY7tLmN2pR8sV","name":"Ana"},"createdAt":"2026-10-12T14:03:11.000Z"},"schema":{"type":"object","properties":{"id":{"type":"string"},"projectId":{"type":"string"},"project":{"type":"string"},"agent":{"$ref":"#/components/schemas/AgentRef"},"title":{"type":["string","null"]},"optionId":{"type":"string"},"expiresAt":{"type":["string","null"],"format":"date-time"},"maxUses":{"type":["integer","null"]},"uses":{"type":"integer"},"lastUsedAt":{"type":["string","null"],"format":"date-time"},"status":{"type":"string","enum":["active","expired","used_up","inactive"]},"createdBy":{"anyOf":[{"$ref":"#/components/schemas/MemberRef"},{"type":"null"}]},"createdAt":{"type":"string","format":"date-time"}},"required":["id","projectId","project","agent","title","optionId","expiresAt","maxUses","uses","lastUsedAt","status","createdBy","createdAt"]}}}},"400":{"$ref":"#/components/responses/BadRequestOrUnknownField"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/v1/rules/{id}":{"delete":{"operationId":"deleteRule","tags":["Rules"],"summary":"Delete a rule","description":"It decides nothing from now on. Asks it decided keep their decision (and `ruleId`). Recorded in the activity log.","security":[{"session":[]}],"x-logu-access":{"human":"owner","agent":false},"parameters":[{"schema":{"type":"string","example":"rul_01K7DC7G9J1L3N5Q7S9U1W3Y5A"},"required":true,"description":"The rule's id (`rul_…`).","name":"id","in":"path"},{"schema":{"type":"string","example":"wsp_01K7D7Y8Z0B2D4F6H8K0M2P4R6"},"required":false,"description":"Humans only: the workspace to act in (default: your personal workspace). Agents always act in their API key's workspace.","name":"Logu-Workspace","in":"header"}],"responses":{"204":{"description":"Deleted.","headers":{"Logu-Request-Id":{"$ref":"#/components/headers/RequestId"}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/v1/projects/{id}":{"patch":{"operationId":"muteProject","tags":["Delivery"],"summary":"Mute or unmute a project","description":"For the signed-in member only: a muted project's events aren't delivered to them (they still land in the inbox); asks always reach them.","security":[{"session":[]}],"x-logu-access":{"human":"viewer","agent":false},"parameters":[{"schema":{"type":"string","example":"prj_01K7D9A1C3E5G7J9L1N3Q5S7U9"},"required":true,"description":"The project's id (`prj_…`).","name":"id","in":"path"},{"schema":{"type":"string","example":"wsp_01K7D7Y8Z0B2D4F6H8K0M2P4R6"},"required":false,"description":"Humans only: the workspace to act in (default: your personal workspace). Agents always act in their API key's workspace.","name":"Logu-Workspace","in":"header"}],"requestBody":{"required":true,"content":{"application/json":{"example":{"muted":true},"schema":{"$ref":"#/components/schemas/UpdateProjectInput"}}}},"responses":{"200":{"description":"The project with the member's mute.","headers":{"Logu-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"example":{"id":"prj_01K7D9A1C3E5G7J9L1N3Q5S7U9","name":"shop","muted":true,"routing":{"notify":null,"escalation":null}},"schema":{"$ref":"#/components/schemas/Project"}}}},"400":{"$ref":"#/components/responses/BadRequestOrUnknownField"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/v1/me":{"get":{"operationId":"getMe","tags":["Me"],"summary":"Who is calling","description":"With an API key: the agent, its API key (`apiKey.test`: a test API key, whose asks, events and metrics are shown in the app but never delivered) and workspace, and the limits that apply (field lengths, ask expiry bounds, the longest wait, idempotency key lifetime, rate limits per minute). With a session: the human's account.","security":[{"apiKey":[]},{"session":[]}],"x-agent-hint":"Call once at startup to check the API key works, whether it is a test API key (apiKey.test: nothing you send is delivered), and to read your limits (limits.maxWaitSeconds, limits.asksPerMinute…).","x-logu-access":{"human":"account","agent":true},"responses":{"200":{"description":"The agent (`kind: \"agent\"`) or the human (`kind: \"human\"`).","headers":{"Logu-Request-Id":{"$ref":"#/components/headers/RequestId"},"RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"RateLimit-Policy":{"$ref":"#/components/headers/RateLimitPolicy"}},"content":{"application/json":{"examples":{"agent":{"summary":"An API key","value":{"kind":"agent","agent":{"id":"agt_01K7D8F2H4K6M8P0R2T4V6X8Z0","name":"deploy-bot"},"apiKey":{"id":"key_01K7D8F2J5N7Q9S1U3W5Y7A9C1","name":"deploy-bot","prefix":"lg_8fK2xQp","test":false},"workspace":{"id":"wsp_Xq3bY7tLmN2pR8sV","name":"Ana"},"limits":{"nameLength":64,"titleLength":200,"bodyLength":10000,"maxOptions":6,"decisionTextLength":5000,"defaultAskTimeoutSeconds":86400,"maxAskTimeoutSeconds":2592000,"minAskTimeoutSeconds":10,"maxWaitSeconds":50,"maxAlertsPerMetric":10,"idempotencyKeyTtlSeconds":86400,"maxWebhookEndpoints":10,"maxRules":100,"maxRoutedMembers":50,"maxEscalationMinutes":43200,"retentionDays":180,"maxAttachments":4,"maxAttachmentBytes":5242880,"unattachedAttachmentTtlSeconds":86400,"attachmentUrlTtlSeconds":3600,"attachmentNameLength":200,"maxLongPollsPerWorkspace":150,"maxLongPollsPerAsk":5,"maxStreamsPerWorkspace":200,"streamBusyCloseCode":1013,"busyRetryAfterSeconds":3,"overloadedRetryAfterSeconds":5,"maxQueuedStreamMessages":1000,"minPollIntervalMs":1000,"asksPerMinute":60,"eventsPerMinute":600,"attachmentsPerMinute":120,"requestsPerMinute":1200,"workspaceRequestsPerMinute":3000,"deliveryTestsPerMinute":5,"checkoutsPerHour":5},"locale":"pt-BR"}},"human":{"summary":"A signed-in human","value":{"kind":"human","id":"Xq3bY7tLmN2pR8sV","name":"Ana","email":"ana@example.com","emailVerified":true,"locale":"pt-BR"}}},"schema":{"anyOf":[{"$ref":"#/components/schemas/AgentMe"},{"$ref":"#/components/schemas/HumanMe"}]}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/WorkspaceSuspended"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/v1/account":{"get":{"operationId":"getAccount","tags":["Me"],"summary":"Your account: deleting it and exporting your data","description":"What Settings → Account needs: whether the account can be deleted now (`deletion.blockers` lists the shared workspaces where you are the only Owner of a team, and workspaces with a live Pro subscription), which shared workspaces would be deleted with it, and the latest data export. Humans only.","security":[{"session":[]}],"x-logu-access":{"human":"account","agent":false},"responses":{"200":{"description":"The account overview.","headers":{"Logu-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"example":{"email":"ana@example.com","deletion":{"graceDays":30,"blockers":[{"kind":"sole_owner","workspace":{"id":"wsp_01J9Z3K4X8V6Q2N5R7T0W1Y3B6","name":"Acme"},"otherMembers":2}],"deletesWorkspaces":[]},"export":{"id":"exp_01J9Z3K4X8V6Q2N5R7T0W1Y3B5","status":"ready","requestedAt":"2026-10-12T14:03:11.000Z","readyAt":"2026-10-12T14:05:42.000Z","expiresAt":"2026-10-19T14:05:42.000Z","size":48213,"downloadUrl":"https://api.logu.dev/x/exp_01J9Z3K4X8V6Q2N5R7T0W1Y3B5?exp=1791900000&sig=Zm9yLWRvY3Mtb25seQ","downloadUrlExpiresAt":"2026-10-13T14:05:42.000Z"},"privacyContact":"privacidade@logu.dev"},"schema":{"type":"object","properties":{"email":{"type":"string"},"deletion":{"type":"object","properties":{"graceDays":{"type":"integer"},"blockers":{"type":"array","items":{"type":"object","properties":{"kind":{"type":"string","enum":["sole_owner","subscription"],"description":"`sole_owner`: you're the only Owner of a shared workspace that has other members (transfer ownership or delete the workspace). `subscription`: a workspace that would be deleted with the account has a live Pro subscription (cancel it first)."},"workspace":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"}},"required":["id","name"]},"otherMembers":{"type":"integer"}},"required":["kind","workspace","otherMembers"]}},"deletesWorkspaces":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"}},"required":["id","name"]}}},"required":["graceDays","blockers","deletesWorkspaces"]},"export":{"type":["object","null"],"properties":{"id":{"type":"string"},"status":{"type":"string","enum":["processing","ready","failed"]},"requestedAt":{"type":"string","format":"date-time"},"readyAt":{"type":["string","null"],"format":"date-time"},"expiresAt":{"type":["string","null"],"format":"date-time"},"size":{"type":["integer","null"]},"downloadUrl":{"type":["string","null"]},"downloadUrlExpiresAt":{"type":["string","null"],"format":"date-time"}},"required":["id","status","requestedAt","readyAt","expiresAt","size","downloadUrl","downloadUrlExpiresAt"]},"privacyContact":{"type":"string"}},"required":["email","deletion","export","privacyContact"]}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/v1/account/deletion":{"post":{"operationId":"requestAccountDeletion","tags":["Me"],"summary":"Delete my account","description":"Starts a 30-day grace: the account is disabled now (every session ends, phones and browsers stop receiving pushes, you stop getting asks in shared workspaces) and deleted for good at `purgeAt` unless you restore it by asking for a sign-in link at logu.dev (the link restores the account). Your personal workspace and everything in it go with the account; in shared workspaces your decisions and activity stay, with your name replaced by \"Removed member\". Needs a session started in the last 15 minutes and the account's email typed in `email`. Refused with `conflict` while you are the only Owner of a shared workspace that has other members (hint: transfer ownership or delete it) or a workspace to be deleted has a live Pro subscription (hint: cancel it first).","security":[{"session":[]}],"x-logu-access":{"human":"account","agent":false},"requestBody":{"required":true,"content":{"application/json":{"example":{"email":"ana@example.com"},"schema":{"type":"object","properties":{"email":{"type":"string","minLength":1,"maxLength":254}},"required":["email"],"additionalProperties":false}}}},"responses":{"202":{"description":"The request is recorded; the account is disabled.","headers":{"Logu-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"example":{"requestedAt":"2026-10-12T14:03:11.000Z","purgeAt":"2026-11-11T14:03:11.000Z","graceDays":30},"schema":{"type":"object","properties":{"requestedAt":{"type":"string","format":"date-time"},"purgeAt":{"type":"string","format":"date-time"},"graceDays":{"type":"integer"}},"required":["requestedAt","purgeAt","graceDays"]}}}},"400":{"$ref":"#/components/responses/BadRequestOrUnknownField"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"409":{"$ref":"#/components/responses/Conflict"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/v1/account/export":{"post":{"operationId":"exportAccountData","tags":["Me"],"summary":"Export my data","description":"Builds a ZIP of JSON files with your profile, preferences, memberships, sessions, delivery settings, decisions, activity, invitations and personal workspace (asks, events, metrics, agents, a list of your images with links valid for 24 hours). Secrets are never included. It is prepared in the background: read `GET /v1/account` (`export`) until `status` is `ready`, or wait for the email, which has a download link valid for 24 hours; the file is kept for 7 days. One export at a time and one a day. Needs a session started in the last 15 minutes.","security":[{"session":[]}],"x-logu-access":{"human":"account","agent":false},"responses":{"202":{"description":"The export is being prepared.","headers":{"Logu-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"example":{"id":"exp_01J9Z3K4X8V6Q2N5R7T0W1Y3B5","status":"ready","requestedAt":"2026-10-12T14:03:11.000Z","readyAt":"2026-10-12T14:05:42.000Z","expiresAt":"2026-10-19T14:05:42.000Z","size":48213,"downloadUrl":"https://api.logu.dev/x/exp_01J9Z3K4X8V6Q2N5R7T0W1Y3B5?exp=1791900000&sig=Zm9yLWRvY3Mtb25seQ","downloadUrlExpiresAt":"2026-10-13T14:05:42.000Z"},"schema":{"type":"object","properties":{"id":{"type":"string"},"status":{"type":"string","enum":["processing","ready","failed"]},"requestedAt":{"type":"string","format":"date-time"},"readyAt":{"type":["string","null"],"format":"date-time"},"expiresAt":{"type":["string","null"],"format":"date-time"},"size":{"type":["integer","null"]},"downloadUrl":{"type":["string","null"]},"downloadUrlExpiresAt":{"type":["string","null"],"format":"date-time"}},"required":["id","status","requestedAt","readyAt","expiresAt","size","downloadUrl","downloadUrlExpiresAt"]}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"409":{"$ref":"#/components/responses/Conflict"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/v1/agents":{"get":{"operationId":"listAgents","tags":["Agents"],"summary":"List agents","description":"The workspace's agents, newest first, each with its active API keys (never their secrets) and how many asks and events it sent in the last 30 days.","security":[{"session":[]}],"x-logu-access":{"human":"decider","agent":false},"parameters":[{"schema":{"type":"string","example":"wsp_01K7D7Y8Z0B2D4F6H8K0M2P4R6"},"required":false,"description":"Humans only: the workspace to act in (default: your personal workspace). Agents always act in their API key's workspace.","name":"Logu-Workspace","in":"header"}],"responses":{"200":{"description":"Agents.","headers":{"Logu-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"example":{"agents":[{"id":"agt_01K7D8F2H4K6M8P0R2T4V6X8Z0","name":"deploy-bot","notificationLevel":"asks_and_events","createdAt":"2026-10-01T09:00:00.000Z","apiKeys":[{"id":"key_01K7DB1A3C5E7G9J1L3N5Q7S9U","name":"Claude","prefix":"lga_","test":false,"createdAt":"2026-10-02T09:00:00.000Z","lastUsedAt":"2026-10-12T14:03:11.000Z","connector":{"clientId":"oac_01K7DB3C5E7G9J1L3N5Q7S9U1W","name":"Claude"}},{"id":"key_01K7D8F2J5N7Q9S1U3W5Y7A9C1","name":"deploy-bot","prefix":"lg_8fK2xQp","test":false,"createdAt":"2026-10-01T09:00:00.000Z","lastUsedAt":"2026-10-12T14:03:11.000Z","connector":null}],"asks30d":14,"events30d":62}]},"schema":{"type":"object","properties":{"agents":{"type":"array","items":{"$ref":"#/components/schemas/Agent"}}},"required":["agents"]}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"500":{"$ref":"#/components/responses/InternalError"}}},"post":{"operationId":"createAgent","tags":["Agents"],"summary":"Create an agent and its first API key","description":"An agent is anything that calls Logu from code (an AI agent, a script, CI). It belongs to the workspace, not to the member who created it. The API key's secret (`lg_…`) is returned exactly once: only its hash is stored. With `test: true` it is a test API key (`lg_test_…`): what the agent sends with it is shown in the app, never delivered.","security":[{"session":[]}],"x-logu-access":{"human":"decider","agent":false},"parameters":[{"schema":{"type":"string","example":"wsp_01K7D7Y8Z0B2D4F6H8K0M2P4R6"},"required":false,"description":"Humans only: the workspace to act in (default: your personal workspace). Agents always act in their API key's workspace.","name":"Logu-Workspace","in":"header"}],"requestBody":{"required":true,"content":{"application/json":{"example":{"name":"deploy-bot"},"schema":{"$ref":"#/components/schemas/CreateAgentInput"}}}},"responses":{"201":{"description":"The agent and its API key, with the secret in `apiKey.key`.","headers":{"Logu-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"example":{"agent":{"id":"agt_01K7D8F2H4K6M8P0R2T4V6X8Z0","name":"deploy-bot","notificationLevel":"asks_and_events","createdAt":"2026-10-01T09:00:00.000Z","apiKeys":[{"id":"key_01K7D8F2J5N7Q9S1U3W5Y7A9C1","name":"deploy-bot","prefix":"lg_8fK2xQp","test":false,"createdAt":"2026-10-01T09:00:00.000Z","lastUsedAt":"2026-10-12T14:03:11.000Z","connector":null}],"asks30d":14,"events30d":62},"apiKey":{"id":"key_01K7D8F2J5N7Q9S1U3W5Y7A9C1","name":"deploy-bot","prefix":"lg_8fK2xQp","test":false,"createdAt":"2026-10-01T09:00:00.000Z","lastUsedAt":null,"connector":null,"key":"lg_8fK2xQp3R7vT9wY1zA4cD6eG8hJ0kL2m"}},"schema":{"$ref":"#/components/schemas/CreateAgentResult"}}}},"400":{"$ref":"#/components/responses/BadRequestOrUnknownField"},"401":{"$ref":"#/components/responses/Unauthorized"},"402":{"$ref":"#/components/responses/PlanLimit"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/v1/agents/{id}":{"patch":{"operationId":"updateAgent","tags":["Agents"],"summary":"Change an agent's notification level","description":"Sets what the agent's events do to people, for the whole workspace: `asks_and_events` (every event is delivered), `asks_only` (events are stored and shown in the app, only asks are delivered) or `silent` (events are stored and shown, never delivered). Asks are always delivered, whatever the level. Agents connected as `claude-code` or `codex` start at `asks_only`, every other agent at `asks_and_events`. Owners and Deciders; recorded in the activity log as `agent.updated`.","security":[{"session":[]}],"x-logu-access":{"human":"decider","agent":false},"parameters":[{"schema":{"type":"string","example":"agt_01K7D8F2H4K6M8P0R2T4V6X8Z0"},"required":true,"description":"The agent's id (`agt_…`).","name":"id","in":"path"},{"schema":{"type":"string","example":"wsp_01K7D7Y8Z0B2D4F6H8K0M2P4R6"},"required":false,"description":"Humans only: the workspace to act in (default: your personal workspace). Agents always act in their API key's workspace.","name":"Logu-Workspace","in":"header"}],"requestBody":{"required":true,"content":{"application/json":{"example":{"notificationLevel":"asks_only"},"schema":{"type":"object","properties":{"notificationLevel":{"type":"string","enum":["asks_and_events","asks_only","silent"]}},"required":["notificationLevel"],"additionalProperties":false}}}},"responses":{"200":{"description":"The agent, with its active API keys (never their secrets).","headers":{"Logu-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"example":{"id":"agt_01K7D8F2H4K6M8P0R2T4V6X8Z0","name":"deploy-bot","notificationLevel":"asks_only","createdAt":"2026-10-01T09:00:00.000Z","apiKeys":[{"id":"key_01K7DB1A3C5E7G9J1L3N5Q7S9U","name":"Claude","prefix":"lga_","test":false,"createdAt":"2026-10-02T09:00:00.000Z","lastUsedAt":"2026-10-12T14:03:11.000Z","connector":{"clientId":"oac_01K7DB3C5E7G9J1L3N5Q7S9U1W","name":"Claude"}},{"id":"key_01K7D8F2J5N7Q9S1U3W5Y7A9C1","name":"deploy-bot","prefix":"lg_8fK2xQp","test":false,"createdAt":"2026-10-01T09:00:00.000Z","lastUsedAt":"2026-10-12T14:03:11.000Z","connector":null}],"asks30d":14,"events30d":62},"schema":{"$ref":"#/components/schemas/Agent"}}}},"400":{"$ref":"#/components/responses/BadRequestOrUnknownField"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"500":{"$ref":"#/components/responses/InternalError"}}},"delete":{"operationId":"deleteAgent","tags":["Agents"],"summary":"Delete an agent","description":"Deletes the agent and revokes its API keys. What it sent stays, without its name.","security":[{"session":[]}],"x-logu-access":{"human":"decider","agent":false},"parameters":[{"schema":{"type":"string","example":"agt_01K7D8F2H4K6M8P0R2T4V6X8Z0"},"required":true,"description":"The agent's id (`agt_…`).","name":"id","in":"path"},{"schema":{"type":"string","example":"wsp_01K7D7Y8Z0B2D4F6H8K0M2P4R6"},"required":false,"description":"Humans only: the workspace to act in (default: your personal workspace). Agents always act in their API key's workspace.","name":"Logu-Workspace","in":"header"}],"responses":{"204":{"description":"Deleted.","headers":{"Logu-Request-Id":{"$ref":"#/components/headers/RequestId"}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/v1/agents/{id}/api-keys":{"post":{"operationId":"createApiKey","tags":["Agents"],"summary":"Create another API key","description":"Another API key for the agent, e.g. to rotate: create the new one, switch the agent to it, then revoke the old one. With `test: true`, a test API key (`lg_test_…`) to build and try an integration: what it sends is marked `test`, shown in the app and never delivered (no push, browser notification, email, Telegram or Slack). The secret is returned exactly once.","security":[{"session":[]}],"x-logu-access":{"human":"decider","agent":false},"parameters":[{"schema":{"type":"string","example":"agt_01K7D8F2H4K6M8P0R2T4V6X8Z0"},"required":true,"description":"The agent's id (`agt_…`).","name":"id","in":"path"},{"schema":{"type":"string","example":"wsp_01K7D7Y8Z0B2D4F6H8K0M2P4R6"},"required":false,"description":"Humans only: the workspace to act in (default: your personal workspace). Agents always act in their API key's workspace.","name":"Logu-Workspace","in":"header"}],"requestBody":{"required":true,"content":{"application/json":{"example":{"name":"deploy-bot (test)","test":true},"schema":{"$ref":"#/components/schemas/CreateApiKeyInput"}}}},"responses":{"201":{"description":"The API key, with the secret in `key`.","headers":{"Logu-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"example":{"id":"key_01K7DR1B3D5F7H9K1M3P5R7T9V","name":"deploy-bot (test)","prefix":"lg_test_Zr5mW1n","test":true,"createdAt":"2026-10-12T14:05:42.000Z","lastUsedAt":null,"connector":null,"key":"lg_test_Zr5mW1nB8cX3vK6qP9sT2uY4wA7dF0gH"},"schema":{"$ref":"#/components/schemas/NewApiKey"}}}},"400":{"$ref":"#/components/responses/BadRequestOrUnknownField"},"401":{"$ref":"#/components/responses/Unauthorized"},"402":{"$ref":"#/components/responses/CeilingReached"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/v1/agents/{id}/api-keys/{keyId}":{"delete":{"operationId":"revokeApiKey","tags":["Agents"],"summary":"Revoke an API key","description":"The API key stops working at once.","security":[{"session":[]}],"x-logu-access":{"human":"decider","agent":false},"parameters":[{"schema":{"type":"string","example":"agt_01K7D8F2H4K6M8P0R2T4V6X8Z0"},"required":true,"description":"The agent's id (`agt_…`).","name":"id","in":"path"},{"schema":{"type":"string","example":"key_01K7D8F2J5N7Q9S1U3W5Y7A9C1"},"required":true,"description":"The API key's id (`key_…`).","name":"keyId","in":"path"},{"schema":{"type":"string","example":"wsp_01K7D7Y8Z0B2D4F6H8K0M2P4R6"},"required":false,"description":"Humans only: the workspace to act in (default: your personal workspace). Agents always act in their API key's workspace.","name":"Logu-Workspace","in":"header"}],"responses":{"204":{"description":"Revoked.","headers":{"Logu-Request-Id":{"$ref":"#/components/headers/RequestId"}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/v1/device/code":{"post":{"operationId":"createDeviceCode","tags":["Agents"],"summary":"Start connecting an agent (device code)","description":"No credentials: how a client without an API key gets one (`logu login`). `client_name` suggests the agent's name and `client_kind` (`claude-code`, `codex`, `cli`) lets the app suggest an existing agent of that kind; `test: true` asks for a test API key (`lg_test_…`, `logu login --test`). Returns a secret `device_code` for the client and a short `user_code` for the human, who opens `verification_uri_complete` (the app's /connect page), checks the code and approves it in a workspace. Then poll `POST /v1/device/token`. Codes expire in 10 minutes. Field names follow RFC 8628. Rate-limited per IP address.","security":[],"x-agent-hint":"Only when you have no API key and a human is at hand: show them user_code and verification_uri_complete (open it in their browser if you can), then poll POST /v1/device/token every `interval` seconds. Never show or log device_code.","x-logu-access":{"human":null,"agent":false,"public":true},"requestBody":{"required":true,"content":{"application/json":{"example":{"client_name":"Claude Code on kevins-mac","client_kind":"claude-code"},"schema":{"type":"object","properties":{"client_name":{"type":"string","minLength":1,"maxLength":64},"client_kind":{"type":"string","enum":["claude-code","codex","cli"]},"test":{"type":"boolean"}},"additionalProperties":false}}}},"responses":{"200":{"description":"The device code, the user code and where the human approves it.","headers":{"Logu-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"example":{"device_code":"GmRhmhcxhwAzkoEqiMEg_DnyEysNkuNhszIySk9eS","user_code":"WDJB-MJHT","verification_uri":"https://logu.dev/connect","verification_uri_complete":"https://logu.dev/connect?code=WDJB-MJHT","expires_in":600,"interval":5},"schema":{"type":"object","properties":{"device_code":{"type":"string"},"user_code":{"type":"string"},"verification_uri":{"type":"string"},"verification_uri_complete":{"type":"string"},"expires_in":{"type":"integer"},"interval":{"type":"integer"}},"required":["device_code","user_code","verification_uri","verification_uri_complete","expires_in","interval"]}}}},"400":{"$ref":"#/components/responses/BadRequestOrUnknownField"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/v1/device/token":{"post":{"operationId":"pollDeviceToken","tags":["Agents"],"summary":"Poll for the connected agent's API key","description":"No credentials. While the human hasn't decided: 400 `authorization_pending` (poll again after `interval` seconds) or `slow_down` (polled too soon: add 5 seconds to the interval). 403 `access_denied` when the human cancelled; 400 `expired_token` when the code expired or its API key was already delivered. On approval: the new agent's API key, returned exactly once.","security":[],"x-agent-hint":"Poll every `interval` seconds (5) until you get the API key; on slow_down add 5 seconds; stop on access_denied or expired_token. Store api_key securely (e.g. ~/.config/logu/config.json, mode 0600): it is never shown again.","x-logu-access":{"human":null,"agent":false,"public":true},"requestBody":{"required":true,"content":{"application/json":{"example":{"device_code":"GmRhmhcxhwAzkoEqiMEg_DnyEysNkuNhszIySk9eS"},"schema":{"type":"object","properties":{"device_code":{"type":"string","minLength":1,"maxLength":200}},"required":["device_code"],"additionalProperties":false}}}},"responses":{"200":{"description":"The API key (`lg_…`), its agent and workspace. Shown this once.","headers":{"Logu-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"example":{"api_key":"lg_8fK2xQp3R7vT9wY1zA4cD6eG8hJ0kL2m","agent":{"id":"agt_01K7D8F2H4K6M8P0R2T4V6X8Z0","name":"Claude Code on kevins-mac"},"workspace":{"id":"wsp_Xq3bY7tLmN2pR8sV","name":"Ana"}},"schema":{"type":"object","properties":{"api_key":{"type":"string"},"agent":{"$ref":"#/components/schemas/AgentRef"},"workspace":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"}},"required":["id","name"]}},"required":["api_key","agent","workspace"]}}}},"400":{"$ref":"#/components/responses/BadRequestOrUnknownFieldOrAuthorizationPendingOrSlowDownOrExpiredToken"},"403":{"$ref":"#/components/responses/AccessDenied"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/v1/device/{userCode}":{"get":{"operationId":"describeDeviceCode","tags":["Agents"],"summary":"Look up a user code","description":"For the app's /connect page: the name and kind the client suggested and when the code expires. 404 for an unknown or expired code, 409 when it was already approved or denied.","security":[{"session":[]}],"x-logu-access":{"human":"account","agent":false},"parameters":[{"schema":{"type":"string","example":"WDJB-MJHT"},"required":true,"description":"The user code the client showed (\"WDJB-MJHT\"; case and dashes don't matter).","name":"userCode","in":"path"}],"responses":{"200":{"description":"The pending request.","headers":{"Logu-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"example":{"userCode":"WDJB-MJHT","clientName":"Claude Code on kevins-mac","clientKind":"claude-code","test":false,"expiresAt":"2026-10-01T09:10:00.000Z"},"schema":{"type":"object","properties":{"userCode":{"type":"string"},"clientName":{"type":"string"},"clientKind":{"type":["string","null"],"enum":["claude-code","codex","cli",null]},"test":{"type":"boolean"},"expiresAt":{"type":"string","format":"date-time"}},"required":["userCode","clientName","clientKind","test","expiresAt"]}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/v1/device/approve":{"post":{"operationId":"approveDeviceCode","tags":["Agents"],"summary":"Connect the agent (approve a user code)","description":"Connects the client in the request's workspace (`Logu-Workspace`): as a new agent named `agentName` (default: the client's suggestion), or with `agentId` as an existing agent of that workspace (404 for another workspace's agent or a deleted one). The client's next poll of `POST /v1/device/token` receives a new API key for that agent, once: a new agent's first API key is named after it, one for an existing agent after the client (\"Codex on kevins-mac\"); the agent's other API keys keep working. Deciders and Owners only. Recorded in the activity log (`agent.created` for a new agent, `agent.connected`, then `api_key.created` with `via: connect`).","security":[{"session":[]}],"x-logu-access":{"human":"decider","agent":false},"parameters":[{"schema":{"type":"string","example":"wsp_01K7D7Y8Z0B2D4F6H8K0M2P4R6"},"required":false,"description":"Humans only: the workspace to act in (default: your personal workspace). Agents always act in their API key's workspace.","name":"Logu-Workspace","in":"header"}],"requestBody":{"required":true,"content":{"application/json":{"example":{"userCode":"WDJB-MJHT","agentName":"Claude Code on kevins-mac"},"schema":{"type":"object","properties":{"userCode":{"type":"string","maxLength":32},"agentName":{"type":"string","minLength":1,"maxLength":64},"agentId":{"type":"string","minLength":1,"maxLength":64}},"required":["userCode"],"additionalProperties":false}}}},"responses":{"200":{"description":"The agent (new or existing) and its workspace.","headers":{"Logu-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"examples":{"new":{"summary":"As a new agent","value":{"agent":{"id":"agt_01K7D8F2H4K6M8P0R2T4V6X8Z0","name":"Claude Code on kevins-mac"},"existing":false,"workspace":{"id":"wsp_Xq3bY7tLmN2pR8sV","name":"Ana"}}},"existing":{"summary":"As the existing agent Codex","value":{"agent":{"id":"agt_01K7D8F2H4K6M8P0R2T4V6X8Z0","name":"Codex"},"existing":true,"workspace":{"id":"wsp_Xq3bY7tLmN2pR8sV","name":"Ana"}}}},"schema":{"type":"object","properties":{"agent":{"$ref":"#/components/schemas/AgentRef"},"existing":{"type":"boolean"},"workspace":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"}},"required":["id","name"]}},"required":["agent","existing","workspace"]}}}},"400":{"$ref":"#/components/responses/BadRequestOrUnknownField"},"401":{"$ref":"#/components/responses/Unauthorized"},"402":{"$ref":"#/components/responses/PlanLimitOrCeilingReached"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/v1/device/deny":{"post":{"operationId":"denyDeviceCode","tags":["Agents"],"summary":"Cancel connecting an agent","description":"Refuses the user code: the client's next poll gets `access_denied` and stops. Nothing is created.","security":[{"session":[]}],"x-logu-access":{"human":"account","agent":false},"requestBody":{"required":true,"content":{"application/json":{"example":{"userCode":"WDJB-MJHT"},"schema":{"type":"object","properties":{"userCode":{"type":"string","maxLength":32}},"required":["userCode"],"additionalProperties":false}}}},"responses":{"204":{"description":"Denied.","headers":{"Logu-Request-Id":{"$ref":"#/components/headers/RequestId"}}},"400":{"$ref":"#/components/responses/BadRequestOrUnknownField"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"409":{"$ref":"#/components/responses/Conflict"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/v1/oauth/requests/{id}":{"get":{"operationId":"describeOAuthRequest","tags":["Agents"],"summary":"Look up a connector request","description":"For the app's /connect page, where an MCP client (claude.ai, ChatGPT, Cursor…) sends the human after `GET /oauth/authorize`: the name the client registered, the host its redirect goes to (`local` when it's this computer) and when the request expires (10 minutes). 404 for an unknown or expired request, 409 when it was already approved or cancelled.","security":[{"session":[]}],"x-logu-access":{"human":"account","agent":false},"parameters":[{"schema":{"type":"string","example":"oar_Tq9mZ2xK7pW4vR8nB3cY6dF1gH5jL0sA"},"required":true,"description":"The OAuth request's id (`oar_…`), from the `/connect?request=` link.","name":"id","in":"path"}],"responses":{"200":{"description":"The pending request.","headers":{"Logu-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"example":{"id":"oar_Tq9mZ2xK7pW4vR8nB3cY6dF1gH5jL0sA","clientName":"Claude","clientUri":"https://claude.ai","redirectHost":"claude.ai","local":false,"expiresAt":"2026-10-01T09:10:00.000Z"},"schema":{"type":"object","properties":{"id":{"type":"string"},"clientName":{"type":"string"},"clientUri":{"type":["string","null"]},"redirectHost":{"type":"string"},"local":{"type":"boolean"},"expiresAt":{"type":"string","format":"date-time"}},"required":["id","clientName","clientUri","redirectHost","local","expiresAt"]}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/v1/oauth/requests/{id}/approve":{"post":{"operationId":"approveOAuthRequest","tags":["Agents"],"summary":"Connect a connector","description":"Connects the MCP client in the request's workspace (`Logu-Workspace`): as a new agent named `agentName` (default: the client's name), or with `agentId` as an existing agent of that workspace. Returns `redirectTo`, the client's redirect URI with a one-time code (2 minutes): the app sends the browser there, and the client exchanges the code at `POST /oauth/token`. The connector then shows among the agent's API keys (`connector` set); revoking it or deleting the agent disconnects the client. Deciders and Owners only. Recorded in the activity log (`agent.created` for a new agent, `agent.connected`, then `api_key.created`, each with `via: oauth`).","security":[{"session":[]}],"x-logu-access":{"human":"decider","agent":false},"parameters":[{"schema":{"type":"string","example":"oar_Tq9mZ2xK7pW4vR8nB3cY6dF1gH5jL0sA"},"required":true,"description":"The OAuth request's id (`oar_…`), from the `/connect?request=` link.","name":"id","in":"path"},{"schema":{"type":"string","example":"wsp_01K7D7Y8Z0B2D4F6H8K0M2P4R6"},"required":false,"description":"Humans only: the workspace to act in (default: your personal workspace). Agents always act in their API key's workspace.","name":"Logu-Workspace","in":"header"}],"requestBody":{"required":true,"content":{"application/json":{"example":{"agentName":"Claude"},"schema":{"type":"object","properties":{"agentName":{"type":"string","minLength":1,"maxLength":64},"agentId":{"type":"string","minLength":1,"maxLength":64}},"additionalProperties":false}}}},"responses":{"200":{"description":"The agent (new or existing), its workspace and where to send the browser.","headers":{"Logu-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"example":{"agent":{"id":"agt_01K7D8F2H4K6M8P0R2T4V6X8Z0","name":"Claude"},"existing":false,"workspace":{"id":"wsp_Xq3bY7tLmN2pR8sV","name":"Ana"},"redirectTo":"https://claude.ai/api/mcp/auth_callback?code=Yk3Jq8pW2vR6tN0mX4cB7dF9gH1jK5lZ&state=af0ifjsldkj&iss=https%3A%2F%2Fapi.logu.dev"},"schema":{"type":"object","properties":{"agent":{"$ref":"#/components/schemas/AgentRef"},"existing":{"type":"boolean"},"workspace":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"}},"required":["id","name"]},"redirectTo":{"type":"string"}},"required":["agent","existing","workspace","redirectTo"]}}}},"400":{"$ref":"#/components/responses/BadRequestOrUnknownField"},"401":{"$ref":"#/components/responses/Unauthorized"},"402":{"$ref":"#/components/responses/PlanLimitOrCeilingReached"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/v1/oauth/requests/{id}/deny":{"post":{"operationId":"denyOAuthRequest","tags":["Agents"],"summary":"Cancel connecting a connector","description":"Refuses the request: nothing is created, and `redirectTo` sends the browser back to the client with `error=access_denied`.","security":[{"session":[]}],"x-logu-access":{"human":"account","agent":false},"parameters":[{"schema":{"type":"string","example":"oar_Tq9mZ2xK7pW4vR8nB3cY6dF1gH5jL0sA"},"required":true,"description":"The OAuth request's id (`oar_…`), from the `/connect?request=` link.","name":"id","in":"path"}],"responses":{"200":{"description":"Where to send the browser.","headers":{"Logu-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"example":{"redirectTo":"https://claude.ai/api/mcp/auth_callback?error=access_denied&error_description=The+human+cancelled+connecting&state=af0ifjsldkj&iss=https%3A%2F%2Fapi.logu.dev"},"schema":{"type":"object","properties":{"redirectTo":{"type":"string"}},"required":["redirectTo"]}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/v1/devices":{"post":{"operationId":"registerDevice","tags":["Delivery"],"summary":"Register a phone for push","description":"The app registers its FCM token on every launch. Re-registering a token moves it to the signed-in member.","security":[{"session":[]}],"x-logu-access":{"human":"account","agent":false},"requestBody":{"required":true,"content":{"application/json":{"example":{"platform":"ios","token":"fcm:dQw4w9WgXcQ8vN2pL5kR7tY1uI3oP6aS9dF2gH4jK"},"schema":{"$ref":"#/components/schemas/RegisterDeviceInput"}}}},"responses":{"201":{"description":"The device's id.","headers":{"Logu-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"example":{"id":"dev_01K7DA2B4D6F8H0K2M4P6R8T0V"},"schema":{"type":"object","properties":{"id":{"type":"string"}},"required":["id"]}}}},"400":{"$ref":"#/components/responses/BadRequestOrUnknownField"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/v1/devices/{id}":{"delete":{"operationId":"removeDevice","tags":["Delivery"],"summary":"Remove a device","description":"Stops push to that phone.","security":[{"session":[]}],"x-logu-access":{"human":"account","agent":false},"parameters":[{"schema":{"type":"string","example":"dev_01K7DA2B4D6F8H0K2M4P6R8T0V"},"required":true,"description":"The device's id (`dev_…`).","name":"id","in":"path"}],"responses":{"204":{"description":"Removed.","headers":{"Logu-Request-Id":{"$ref":"#/components/headers/RequestId"}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/v1/push/config":{"get":{"operationId":"getPushConfig","tags":["Delivery"],"summary":"Push configuration","description":"What the apps need to set up push: the VAPID public key for browser notifications, or null when this server has no browser push, and how many phones the member has registered for push.","security":[{"session":[]}],"x-logu-access":{"human":"account","agent":false},"responses":{"200":{"description":"The push configuration.","headers":{"Logu-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"example":{"webPushPublicKey":"BNcRdreALRFXTkOOUHK1EtK2wtaz5Ry4YfYCA_0QTpQtUbVlUls0VJXg7A8u-Ts1XbjhazAkj7I99e8QcYP7DkM","phones":1},"schema":{"type":"object","properties":{"webPushPublicKey":{"type":["string","null"]},"phones":{"type":"integer","minimum":0}},"required":["webPushPublicKey","phones"]}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/v1/web-push/subscriptions":{"post":{"operationId":"subscribeWebPush","tags":["Delivery"],"summary":"Turn on browser notifications","description":"Subscribes this browser (its `PushSubscription.toJSON()`). Re-subscribing an endpoint moves it to the signed-in member.","security":[{"session":[]}],"x-logu-access":{"human":"account","agent":false},"requestBody":{"required":true,"content":{"application/json":{"example":{"endpoint":"https://fcm.googleapis.com/fcm/send/c1KrmpTuRm4:APA91bGq3nYdF8wN2xH5kL7mP9rT1vY3zB6","expirationTime":null,"keys":{"p256dh":"BOr0Wq5bNn7m3VPZqM2Qv2qYb4yLw8M3Kp5uR7tY9wA1cE3gH5jK7mN9pR1tV3xZ5bD7fH9kL1nP3rT5vX7zB9dF","auth":"k8JV6sjdbhAi-n3Qzm2zqA"}},"schema":{"$ref":"#/components/schemas/WebPushSubscriptionInput"}}}},"responses":{"201":{"description":"The subscription's id.","headers":{"Logu-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"example":{"id":"wps_01K7DA3C5E7G9J1L3N5Q7S9U1W"},"schema":{"type":"object","properties":{"id":{"type":"string"}},"required":["id"]}}}},"400":{"$ref":"#/components/responses/BadRequestOrUnknownField"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"$ref":"#/components/responses/ServiceUnavailable"}}},"delete":{"operationId":"unsubscribeWebPush","tags":["Delivery"],"summary":"Turn off browser notifications","description":"Removes this browser's subscription (no error if there was none).","security":[{"session":[]}],"x-logu-access":{"human":"account","agent":false},"requestBody":{"required":true,"content":{"application/json":{"example":{"endpoint":"https://fcm.googleapis.com/fcm/send/c1KrmpTuRm4:APA91bGq3nYdF8wN2xH5kL7mP9rT1vY3zB6"},"schema":{"$ref":"#/components/schemas/UnsubscribeWebPushInput"}}}},"responses":{"204":{"description":"Removed.","headers":{"Logu-Request-Id":{"$ref":"#/components/headers/RequestId"}}},"400":{"$ref":"#/components/responses/BadRequestOrUnknownField"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/v1/webhooks":{"get":{"operationId":"listWebhookEndpoints","tags":["Webhooks"],"summary":"List webhook endpoints","description":"The workspace's webhook endpoints, newest first, never with their signing secrets. Owners only.","security":[{"session":[]}],"x-logu-access":{"human":"owner","agent":false},"parameters":[{"schema":{"type":"string","example":"wsp_01K7D7Y8Z0B2D4F6H8K0M2P4R6"},"required":false,"description":"Humans only: the workspace to act in (default: your personal workspace). Agents always act in their API key's workspace.","name":"Logu-Workspace","in":"header"}],"responses":{"200":{"description":"Webhook endpoints.","headers":{"Logu-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"example":{"webhooks":[{"id":"whk_01K7DC5E7G9J1L3N5Q7S9U1W3Y","url":"https://hooks.acme.example/logu","description":"Ops dashboard","eventTypes":["ask.settled","event.created"],"enabled":true,"disabledReason":null,"failingSince":null,"previousSecretExpiresAt":null,"createdAt":"2026-10-02T10:00:00.000Z","updatedAt":"2026-10-02T10:00:00.000Z"}]},"schema":{"type":"object","properties":{"webhooks":{"type":"array","items":{"$ref":"#/components/schemas/WebhookEndpoint"}}},"required":["webhooks"]}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"500":{"$ref":"#/components/responses/InternalError"}}},"post":{"operationId":"createWebhookEndpoint","tags":["Webhooks"],"summary":"Create a webhook endpoint","description":"A URL that gets the event types it subscribes to (`ask.created`, `ask.settled`, `event.created`, `metric.alert`) as signed POSTs, per Standard Webhooks: `webhook-id`, `webhook-timestamp` and `webhook-signature` headers. Its signing secret (`whsec_…`) is returned exactly once; it doesn't depend on any API key. The URL must be a public `https:` URL. Up to 10 per workspace.","security":[{"session":[]}],"x-logu-access":{"human":"owner","agent":false},"parameters":[{"schema":{"type":"string","example":"wsp_01K7D7Y8Z0B2D4F6H8K0M2P4R6"},"required":false,"description":"Humans only: the workspace to act in (default: your personal workspace). Agents always act in their API key's workspace.","name":"Logu-Workspace","in":"header"}],"requestBody":{"required":true,"content":{"application/json":{"example":{"url":"https://hooks.acme.example/logu","description":"Ops dashboard","eventTypes":["ask.settled","event.created"]},"schema":{"$ref":"#/components/schemas/CreateWebhookEndpointInput"}}}},"responses":{"201":{"description":"The endpoint, with its signing secret in `secret`.","headers":{"Logu-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"example":{"id":"whk_01K7DC5E7G9J1L3N5Q7S9U1W3Y","url":"https://hooks.acme.example/logu","description":"Ops dashboard","eventTypes":["ask.settled","event.created"],"enabled":true,"disabledReason":null,"failingSince":null,"previousSecretExpiresAt":null,"createdAt":"2026-10-02T10:00:00.000Z","updatedAt":"2026-10-02T10:00:00.000Z","secret":"whsec_MfKQ9r8GKYqrTwjUPD8ILPZIo2LaLaSwq2Lp3n7vX0k="},"schema":{"$ref":"#/components/schemas/WebhookEndpointWithSecret"}}}},"400":{"$ref":"#/components/responses/BadRequestOrUnknownField"},"401":{"$ref":"#/components/responses/Unauthorized"},"402":{"$ref":"#/components/responses/PlanLimit"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/v1/webhooks/{id}":{"get":{"operationId":"getWebhookEndpoint","tags":["Webhooks"],"summary":"Get a webhook endpoint","description":"One webhook endpoint, never with its signing secret.","security":[{"session":[]}],"x-logu-access":{"human":"owner","agent":false},"parameters":[{"schema":{"type":"string","example":"whk_01K7DC5E7G9J1L3N5Q7S9U1W3Y"},"required":true,"description":"The webhook endpoint's id (`whk_…`).","name":"id","in":"path"},{"schema":{"type":"string","example":"wsp_01K7D7Y8Z0B2D4F6H8K0M2P4R6"},"required":false,"description":"Humans only: the workspace to act in (default: your personal workspace). Agents always act in their API key's workspace.","name":"Logu-Workspace","in":"header"}],"responses":{"200":{"description":"The webhook endpoint.","headers":{"Logu-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"example":{"id":"whk_01K7DC5E7G9J1L3N5Q7S9U1W3Y","url":"https://hooks.acme.example/logu","description":"Ops dashboard","eventTypes":["ask.settled","event.created"],"enabled":true,"disabledReason":null,"failingSince":null,"previousSecretExpiresAt":null,"createdAt":"2026-10-02T10:00:00.000Z","updatedAt":"2026-10-02T10:00:00.000Z"},"schema":{"$ref":"#/components/schemas/WebhookEndpoint"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"500":{"$ref":"#/components/responses/InternalError"}}},"patch":{"operationId":"updateWebhookEndpoint","tags":["Webhooks"],"summary":"Change a webhook endpoint","description":"Change its URL, description or event types, or turn it off and on. Turning on an endpoint Logu turned off (`disabledReason: \"failing\"`) clears its failure streak.","security":[{"session":[]}],"x-logu-access":{"human":"owner","agent":false},"parameters":[{"schema":{"type":"string","example":"whk_01K7DC5E7G9J1L3N5Q7S9U1W3Y"},"required":true,"description":"The webhook endpoint's id (`whk_…`).","name":"id","in":"path"},{"schema":{"type":"string","example":"wsp_01K7D7Y8Z0B2D4F6H8K0M2P4R6"},"required":false,"description":"Humans only: the workspace to act in (default: your personal workspace). Agents always act in their API key's workspace.","name":"Logu-Workspace","in":"header"}],"requestBody":{"required":true,"content":{"application/json":{"example":{"enabled":false},"schema":{"$ref":"#/components/schemas/UpdateWebhookEndpointInput"}}}},"responses":{"200":{"description":"The webhook endpoint as it is now.","headers":{"Logu-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"example":{"id":"whk_01K7DC5E7G9J1L3N5Q7S9U1W3Y","url":"https://hooks.acme.example/logu","description":"Ops dashboard","eventTypes":["ask.settled","event.created"],"enabled":false,"disabledReason":"manual","failingSince":null,"previousSecretExpiresAt":null,"createdAt":"2026-10-02T10:00:00.000Z","updatedAt":"2026-10-12T14:05:42.000Z"},"schema":{"$ref":"#/components/schemas/WebhookEndpoint"}}}},"400":{"$ref":"#/components/responses/BadRequestOrUnknownField"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"500":{"$ref":"#/components/responses/InternalError"}}},"delete":{"operationId":"deleteWebhookEndpoint","tags":["Webhooks"],"summary":"Delete a webhook endpoint","description":"Stops sending to it at once and deletes its delivery log.","security":[{"session":[]}],"x-logu-access":{"human":"owner","agent":false},"parameters":[{"schema":{"type":"string","example":"whk_01K7DC5E7G9J1L3N5Q7S9U1W3Y"},"required":true,"description":"The webhook endpoint's id (`whk_…`).","name":"id","in":"path"},{"schema":{"type":"string","example":"wsp_01K7D7Y8Z0B2D4F6H8K0M2P4R6"},"required":false,"description":"Humans only: the workspace to act in (default: your personal workspace). Agents always act in their API key's workspace.","name":"Logu-Workspace","in":"header"}],"responses":{"204":{"description":"Deleted.","headers":{"Logu-Request-Id":{"$ref":"#/components/headers/RequestId"}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/v1/webhooks/{id}/rotate-secret":{"post":{"operationId":"rotateWebhookSecret","tags":["Webhooks"],"summary":"Rotate the signing secret","description":"A new signing secret, returned exactly once. The previous one keeps signing too for 24 hours (`previousSecretExpiresAt`): meanwhile `webhook-signature` carries both signatures, so the receiver can switch without missing a delivery.","security":[{"session":[]}],"x-logu-access":{"human":"owner","agent":false},"parameters":[{"schema":{"type":"string","example":"whk_01K7DC5E7G9J1L3N5Q7S9U1W3Y"},"required":true,"description":"The webhook endpoint's id (`whk_…`).","name":"id","in":"path"},{"schema":{"type":"string","example":"wsp_01K7D7Y8Z0B2D4F6H8K0M2P4R6"},"required":false,"description":"Humans only: the workspace to act in (default: your personal workspace). Agents always act in their API key's workspace.","name":"Logu-Workspace","in":"header"}],"responses":{"200":{"description":"The endpoint, with its new signing secret in `secret`.","headers":{"Logu-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"example":{"id":"whk_01K7DC5E7G9J1L3N5Q7S9U1W3Y","url":"https://hooks.acme.example/logu","description":"Ops dashboard","eventTypes":["ask.settled","event.created"],"enabled":true,"disabledReason":null,"failingSince":null,"previousSecretExpiresAt":"2026-10-13T14:05:42.000Z","createdAt":"2026-10-02T10:00:00.000Z","updatedAt":"2026-10-12T14:05:42.000Z","secret":"whsec_Hk3mP9qR2sT5vW8yA1bC4dE7fG0hJ3kLz8Qw1eR4tY6="},"schema":{"$ref":"#/components/schemas/WebhookEndpointWithSecret"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/v1/webhooks/{id}/test":{"post":{"operationId":"testWebhookEndpoint","tags":["Webhooks"],"summary":"Send a test","description":"Sends one `webhook.test` delivery now (also to an endpoint that is turned off), without retries, and answers with it and its attempt: the receiver's status, or why it got none. Test sends don't count toward turning a failing endpoint off.","security":[{"session":[]}],"x-logu-access":{"human":"owner","agent":false},"parameters":[{"schema":{"type":"string","example":"whk_01K7DC5E7G9J1L3N5Q7S9U1W3Y"},"required":true,"description":"The webhook endpoint's id (`whk_…`).","name":"id","in":"path"},{"schema":{"type":"string","example":"wsp_01K7D7Y8Z0B2D4F6H8K0M2P4R6"},"required":false,"description":"Humans only: the workspace to act in (default: your personal workspace). Agents always act in their API key's workspace.","name":"Logu-Workspace","in":"header"}],"responses":{"200":{"description":"The test delivery, `succeeded` or `failed`, with its attempt.","headers":{"Logu-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"example":{"id":"whd_01K7DR2C4E6G8J0L2N4Q6S8U0W","endpointId":"whk_01K7DC5E7G9J1L3N5Q7S9U1W3Y","type":"webhook.test","status":"succeeded","attemptCount":1,"lastStatusCode":200,"lastAttemptAt":"2026-10-12T14:05:42.000Z","nextAttemptAt":null,"createdAt":"2026-10-12T14:05:42.000Z","payload":{"type":"webhook.test","timestamp":"2026-10-12T14:05:42.000Z","data":{"endpointId":"whk_01K7DC5E7G9J1L3N5Q7S9U1W3Y","message":"A test from Logu: this webhook endpoint is set up."}},"attempts":[{"number":1,"at":"2026-10-12T14:05:42.000Z","statusCode":200,"durationMs":93,"response":"ok","error":null}]},"schema":{"$ref":"#/components/schemas/WebhookDeliveryDetail"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/v1/webhooks/{id}/deliveries":{"get":{"operationId":"listWebhookDeliveries","tags":["Webhooks"],"summary":"Delivery log","description":"The endpoint's webhook deliveries of the last 30 days, newest first: type, status, attempts, the last status code and when the next retry goes out.","security":[{"session":[]}],"x-logu-access":{"human":"owner","agent":false},"parameters":[{"schema":{"type":"string","example":"whk_01K7DC5E7G9J1L3N5Q7S9U1W3Y"},"required":true,"description":"The webhook endpoint's id (`whk_…`).","name":"id","in":"path"},{"schema":{"type":"integer","minimum":1,"maximum":100,"default":50,"example":50},"required":false,"description":"How many to return (1–100).","name":"limit","in":"query"},{"schema":{"type":"string","pattern":"^whd_[A-Za-z0-9]+$","example":"whd_01K7DQ3M2X1K3M5P7R9T1V3X5Z"},"required":false,"description":"The last delivery id of the previous page (newest first).","name":"before","in":"query"},{"schema":{"type":"string","example":"wsp_01K7D7Y8Z0B2D4F6H8K0M2P4R6"},"required":false,"description":"Humans only: the workspace to act in (default: your personal workspace). Agents always act in their API key's workspace.","name":"Logu-Workspace","in":"header"}],"responses":{"200":{"description":"Webhook deliveries, newest first.","headers":{"Logu-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"example":{"deliveries":[{"id":"whd_01K7DQ3M2X1K3M5P7R9T1V3X5Z","endpointId":"whk_01K7DC5E7G9J1L3N5Q7S9U1W3Y","type":"ask.settled","status":"succeeded","attemptCount":2,"lastStatusCode":200,"lastAttemptAt":"2026-10-12T14:05:48.000Z","nextAttemptAt":null,"createdAt":"2026-10-12T14:05:42.000Z"}]},"schema":{"type":"object","properties":{"deliveries":{"type":"array","items":{"$ref":"#/components/schemas/WebhookDelivery"}}},"required":["deliveries"]}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/v1/webhooks/{id}/deliveries/{deliveryId}":{"get":{"operationId":"getWebhookDelivery","tags":["Webhooks"],"summary":"Get a webhook delivery","description":"One webhook delivery with the exact payload sent and every attempt (status code, duration, the first 1 KB of the response, or the error). Never the request's headers or the secret.","security":[{"session":[]}],"x-logu-access":{"human":"owner","agent":false},"parameters":[{"schema":{"type":"string","example":"whk_01K7DC5E7G9J1L3N5Q7S9U1W3Y"},"required":true,"description":"The webhook endpoint's id (`whk_…`).","name":"id","in":"path"},{"schema":{"type":"string","example":"whd_01K7DQ3M2X1K3M5P7R9T1V3X5Z"},"required":true,"description":"The webhook delivery's id (`whd_…`, also its `webhook-id` header).","name":"deliveryId","in":"path"},{"schema":{"type":"string","example":"wsp_01K7D7Y8Z0B2D4F6H8K0M2P4R6"},"required":false,"description":"Humans only: the workspace to act in (default: your personal workspace). Agents always act in their API key's workspace.","name":"Logu-Workspace","in":"header"}],"responses":{"200":{"description":"The webhook delivery.","headers":{"Logu-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"example":{"id":"whd_01K7DQ3M2X1K3M5P7R9T1V3X5Z","endpointId":"whk_01K7DC5E7G9J1L3N5Q7S9U1W3Y","type":"ask.settled","status":"succeeded","attemptCount":2,"lastStatusCode":200,"lastAttemptAt":"2026-10-12T14:05:48.000Z","nextAttemptAt":null,"createdAt":"2026-10-12T14:05:42.000Z","payload":{"type":"ask.settled","timestamp":"2026-10-12T14:05:42.000Z","data":{"ask":{"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}}},"attempts":[{"number":1,"at":"2026-10-12T14:05:42.000Z","statusCode":503,"durationMs":412,"response":"Service Unavailable","error":null},{"number":2,"at":"2026-10-12T14:05:48.000Z","statusCode":200,"durationMs":87,"response":"{\"ok\":true}","error":null}]},"schema":{"$ref":"#/components/schemas/WebhookDeliveryDetail"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/v1/webhooks/{id}/deliveries/{deliveryId}/redeliver":{"post":{"operationId":"redeliverWebhook","tags":["Webhooks"],"summary":"Redeliver","description":"Runs a delivery's attempts again (the same payload and `webhook-id`, so a receiver that already handled it can skip it), with the usual retries. 409 `conflict` while it's still being attempted or when the endpoint is turned off.","security":[{"session":[]}],"x-logu-access":{"human":"owner","agent":false},"parameters":[{"schema":{"type":"string","example":"whk_01K7DC5E7G9J1L3N5Q7S9U1W3Y"},"required":true,"description":"The webhook endpoint's id (`whk_…`).","name":"id","in":"path"},{"schema":{"type":"string","example":"whd_01K7DQ3M2X1K3M5P7R9T1V3X5Z"},"required":true,"description":"The webhook delivery's id (`whd_…`, also its `webhook-id` header).","name":"deliveryId","in":"path"},{"schema":{"type":"string","example":"wsp_01K7D7Y8Z0B2D4F6H8K0M2P4R6"},"required":false,"description":"Humans only: the workspace to act in (default: your personal workspace). Agents always act in their API key's workspace.","name":"Logu-Workspace","in":"header"}],"responses":{"202":{"description":"The webhook delivery, `pending` again.","headers":{"Logu-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"example":{"id":"whd_01K7DQ3M2X1K3M5P7R9T1V3X5Z","endpointId":"whk_01K7DC5E7G9J1L3N5Q7S9U1W3Y","type":"ask.settled","status":"pending","attemptCount":2,"lastStatusCode":200,"lastAttemptAt":"2026-10-12T14:05:48.000Z","nextAttemptAt":"2026-10-12T15:00:00.000Z","createdAt":"2026-10-12T14:05:42.000Z"},"schema":{"$ref":"#/components/schemas/WebhookDelivery"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/v1/activity":{"get":{"operationId":"listActivity","tags":["Activity"],"summary":"Activity log","description":"Who did what in the workspace, newest first: asks created and decided (and by whom), API keys created and revoked, members invited and joined, delivery changes. Never secrets. Page with `cursor`: pass the previous page's `nextCursor` until it is null (`before`, the last entry id, still works). An invalid cursor is a 400 (`bad_request`).","security":[{"session":[]}],"x-logu-access":{"human":"viewer","agent":false},"parameters":[{"schema":{"type":"integer","minimum":1,"maximum":200,"default":50,"example":50},"required":false,"description":"How many to return (1–200).","name":"limit","in":"query"},{"schema":{"type":"string","pattern":"^(act|aud)_[A-Za-z0-9]+$","example":"act_01K7DQ3M2W0J2L4N6Q8S0U2W4Y"},"required":false,"description":"The last entry id of the previous page (`act_…`, or `aud_…` for entries before the rename). Prefer `cursor`; not with it.","name":"before","in":"query"},{"schema":{"type":"string","maxLength":200,"example":"YWN0aXZpdHk6MTc5MTgxMzk0MjAwMDphY3RfMDFLN0RRM00yVzBKMkw0TjZROFMwVTJXNFk"},"required":false,"description":"Where this page starts: the `nextCursor` of the previous page, unchanged. Leave it out (or empty) for the first page.","name":"cursor","in":"query"},{"schema":{"type":"string","example":"wsp_01K7D7Y8Z0B2D4F6H8K0M2P4R6"},"required":false,"description":"Humans only: the workspace to act in (default: your personal workspace). Agents always act in their API key's workspace.","name":"Logu-Workspace","in":"header"}],"responses":{"200":{"description":"Entries, newest first, and where the next page starts (null at the end).","headers":{"Logu-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"example":{"entries":[{"id":"act_01K7DQ3M2W0J2L4N6Q8S0U2W4Y","workspaceId":"wsp_Xq3bY7tLmN2pR8sV","action":"ask.decided","actor":{"kind":"human","user":{"id":"Xq3bY7tLmN2pR8sV","name":"Ana"}},"target":{"type":"ask","id":"ask_01K7DQ3M2V8H4N6R9T1W3Y5B7C"},"data":{"project":"shop","optionId":"approve"},"createdAt":"2026-10-12T14:05:42.000Z"}],"nextCursor":"YWN0aXZpdHk6MTc5MTgxMzk0MjAwMDphY3RfMDFLN0RRM00yVzBKMkw0TjZROFMwVTJXNFk"},"schema":{"type":"object","properties":{"entries":{"type":"array","items":{"$ref":"#/components/schemas/ActivityEntry"}},"nextCursor":{"type":["string","null"],"description":"Pass as `cursor` for the next page, unchanged; null at the end of the list."}},"required":["entries","nextCursor"]}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/v1/billing":{"get":{"operationId":"getBilling","tags":["Billing"],"summary":"Get the plan and usage","description":"Owners only. The workspace's plan (`free` or `pro`), its Stripe subscription (status, price tier, period end, whether it cancels at period end), this calendar month's usage (asks, events, metric reports; UTC) and what it has (agents, members, webhook endpoints), the Free plan's limits, and whether Free limits are enforced on this server (`enforced`).","security":[{"session":[]}],"x-logu-access":{"human":"owner","agent":false},"parameters":[{"schema":{"type":"string","example":"wsp_01K7D7Y8Z0B2D4F6H8K0M2P4R6"},"required":false,"description":"Humans only: the workspace to act in (default: your personal workspace). Agents always act in their API key's workspace.","name":"Logu-Workspace","in":"header"}],"responses":{"200":{"description":"The plan and usage.","headers":{"Logu-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"example":{"plan":"pro","comped":false,"enforced":true,"subscription":{"status":"active","tier":"founder_9","priceUsd":9,"currentPeriodEnd":"2026-11-02T12:00:00.000Z","cancelAtPeriodEnd":false},"usage":{"period":"2026-10","asks":342,"events":1880,"metrics":12400,"agents":4,"members":3,"webhookEndpoints":1},"freeLimits":{"members":1,"asksPerMonth":100,"eventsPerMonth":1000,"metricsPerMonth":10000,"agents":2,"webhookEndpoints":0,"historyDays":30},"meters":[{"meter":"asks","used":342,"quota":5000,"ceiling":20000,"state":"ok"},{"meter":"events","used":41200,"quota":50000,"ceiling":200000,"state":"warning"},{"meter":"metrics","used":12400,"quota":250000,"ceiling":1000000,"state":"ok"},{"meter":"storage","used":1288490188,"quota":21474836480,"ceiling":26843545600,"state":"ok"},{"meter":"whatsapp","used":100,"quota":100,"ceiling":500,"state":"full"},{"meter":"emails","used":212,"quota":null,"ceiling":5000,"state":"ok"},{"meter":"projects","used":7,"quota":100,"ceiling":500,"state":"ok"},{"meter":"metricNames","used":31,"quota":500,"ceiling":2000,"state":"ok"}],"deliveryCapped":{"whatsapp":true,"email":false},"checkoutAvailable":true},"schema":{"type":"object","properties":{"plan":{"type":"string","enum":["free","pro"]},"comped":{"type":"boolean"},"enforced":{"type":"boolean"},"subscription":{"type":["object","null"],"properties":{"status":{"type":"string"},"tier":{"type":"string","enum":["founder_9","founder_19","pro_29"]},"priceUsd":{"type":"number"},"currentPeriodEnd":{"type":["string","null"],"format":"date-time"},"cancelAtPeriodEnd":{"type":"boolean"}},"required":["status","tier","priceUsd","currentPeriodEnd","cancelAtPeriodEnd"]},"usage":{"type":"object","properties":{"period":{"type":"string"},"asks":{"type":"integer"},"events":{"type":"integer"},"metrics":{"type":"integer"},"agents":{"type":"integer"},"members":{"type":"integer"},"webhookEndpoints":{"type":"integer"}},"required":["period","asks","events","metrics","agents","members","webhookEndpoints"]},"freeLimits":{"type":"object","properties":{"members":{"type":"integer"},"asksPerMonth":{"type":"integer"},"eventsPerMonth":{"type":"integer"},"metricsPerMonth":{"type":"integer"},"agents":{"type":"integer"},"webhookEndpoints":{"type":"integer"},"historyDays":{"type":"integer"}},"required":["members","asksPerMonth","eventsPerMonth","metricsPerMonth","agents","webhookEndpoints","historyDays"]},"meters":{"type":"array","items":{"type":"object","properties":{"meter":{"type":"string","enum":["asks","events","metrics","storage","whatsapp","emails","images","testAsks","testEvents","testMetrics","projects","metricNames"]},"used":{"type":"integer"},"quota":{"type":["integer","null"]},"ceiling":{"type":"integer"},"state":{"type":"string","enum":["ok","warning","full"]}},"required":["meter","used","quota","ceiling","state"]}},"deliveryCapped":{"type":"object","properties":{"whatsapp":{"type":"boolean"},"email":{"type":"boolean"}},"required":["whatsapp","email"]},"checkoutAvailable":{"type":"boolean"}},"required":["plan","comped","enforced","subscription","usage","freeLimits","meters","deliveryCapped","checkoutAvailable"]}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/v1/billing/checkout":{"post":{"operationId":"startCheckout","tags":["Billing"],"summary":"Upgrade to Pro (Stripe Checkout)","description":"Owners only. A Stripe Checkout page for the workspace's Pro subscription, at the price a workspace subscribing now gets: a founder price while spots last (locked while the subscription stays active), else the list price. The spot is held while the page is open (about 30 minutes); starting again keeps it. Send the human to `url`; they come back to the app's Settings → Plan. 409 when the workspace is already on Pro; 503 when billing isn't configured on this server.","security":[{"session":[]}],"x-logu-access":{"human":"owner","agent":false},"parameters":[{"schema":{"type":"string","example":"wsp_01K7D7Y8Z0B2D4F6H8K0M2P4R6"},"required":false,"description":"Humans only: the workspace to act in (default: your personal workspace). Agents always act in their API key's workspace.","name":"Logu-Workspace","in":"header"}],"responses":{"200":{"description":"Where to send the human.","headers":{"Logu-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"example":{"url":"https://checkout.stripe.com/c/pay/cs_live_a1B2c3D4e5F6"},"schema":{"type":"object","properties":{"url":{"type":"string","format":"uri"}},"required":["url"]}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"$ref":"#/components/responses/ServiceUnavailable"}}}},"/v1/billing/portal":{"post":{"operationId":"openBillingPortal","tags":["Billing"],"summary":"Manage the subscription (Stripe Customer Portal)","description":"Owners only. A Stripe Customer Portal page for the workspace's subscription: payment method, invoices, cancel. 409 when the workspace never subscribed.","security":[{"session":[]}],"x-logu-access":{"human":"owner","agent":false},"parameters":[{"schema":{"type":"string","example":"wsp_01K7D7Y8Z0B2D4F6H8K0M2P4R6"},"required":false,"description":"Humans only: the workspace to act in (default: your personal workspace). Agents always act in their API key's workspace.","name":"Logu-Workspace","in":"header"}],"responses":{"200":{"description":"Where to send the human.","headers":{"Logu-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"example":{"url":"https://billing.stripe.com/p/session/live_YWNjdF8xUDJx"},"schema":{"type":"object","properties":{"url":{"type":"string","format":"uri"}},"required":["url"]}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"$ref":"#/components/responses/ServiceUnavailable"}}}},"/v1/billing/pricing":{"get":{"operationId":"getPricing","tags":["Billing"],"summary":"Get Pro's prices","description":"No credentials. What Pro costs a workspace subscribing now (`current`), each founder tier with its spots left, the list price, and the Free plan's limits.","security":[],"x-logu-access":{"human":null,"agent":false,"public":true},"responses":{"200":{"description":"The prices.","headers":{"Logu-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"example":{"currency":"usd","listPriceUsd":29,"current":{"tier":"founder_9","priceUsd":9,"slots":100,"spotsLeft":63},"tiers":[{"tier":"founder_9","priceUsd":9,"slots":100,"spotsLeft":63},{"tier":"founder_19","priceUsd":19,"slots":900,"spotsLeft":900},{"tier":"pro_29","priceUsd":29,"slots":null,"spotsLeft":null}],"freeLimits":{"members":1,"asksPerMonth":100,"eventsPerMonth":1000,"metricsPerMonth":10000,"agents":2,"webhookEndpoints":0,"historyDays":30}},"schema":{"type":"object","properties":{"currency":{"type":"string","enum":["usd"]},"listPriceUsd":{"type":"number"},"current":{"type":"object","properties":{"tier":{"type":"string","enum":["founder_9","founder_19","pro_29"]},"priceUsd":{"type":"number"},"slots":{"type":["integer","null"]},"spotsLeft":{"type":["integer","null"]}},"required":["tier","priceUsd","slots","spotsLeft"]},"tiers":{"type":"array","items":{"type":"object","properties":{"tier":{"type":"string","enum":["founder_9","founder_19","pro_29"]},"priceUsd":{"type":"number"},"slots":{"type":["integer","null"]},"spotsLeft":{"type":["integer","null"]}},"required":["tier","priceUsd","slots","spotsLeft"]}},"freeLimits":{"type":"object","properties":{"members":{"type":"integer"},"asksPerMonth":{"type":"integer"},"eventsPerMonth":{"type":"integer"},"metricsPerMonth":{"type":"integer"},"agents":{"type":"integer"},"webhookEndpoints":{"type":"integer"},"historyDays":{"type":"integer"}},"required":["members","asksPerMonth","eventsPerMonth","metricsPerMonth","agents","webhookEndpoints","historyDays"]}},"required":["currency","listPriceUsd","current","tiers","freeLimits"]}}}},"500":{"$ref":"#/components/responses/InternalError"}}}},"/v1/telegram":{"get":{"operationId":"getTelegram","tags":["Delivery"],"summary":"Telegram status","description":"Whether this server has a Telegram bot, and whether the member's chat is connected.","security":[{"session":[]}],"x-logu-access":{"human":"account","agent":false},"responses":{"200":{"description":"The member's Telegram status.","headers":{"Logu-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"example":{"available":true,"connected":true,"username":"ana_ops"},"schema":{"$ref":"#/components/schemas/TelegramStatus"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"500":{"$ref":"#/components/responses/InternalError"}}},"delete":{"operationId":"disconnectTelegram","tags":["Delivery"],"summary":"Disconnect Telegram","description":"Stops delivery to the member's Telegram chat.","security":[{"session":[]}],"x-logu-access":{"human":"account","agent":false},"responses":{"204":{"description":"Disconnected.","headers":{"Logu-Request-Id":{"$ref":"#/components/headers/RequestId"}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/v1/telegram/link":{"post":{"operationId":"createTelegramLink","tags":["Delivery"],"summary":"Connect Telegram","description":"A one-time t.me link: opening it and pressing Start connects that Telegram chat to the member.","security":[{"session":[]}],"x-logu-access":{"human":"account","agent":false},"responses":{"201":{"description":"The link and when it expires.","headers":{"Logu-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"example":{"url":"https://t.me/LoguBot?start=Zk3mP9qR2sT5vW8yA1bC4dE7","expiresAt":"2026-10-12T14:13:11.000Z"},"schema":{"$ref":"#/components/schemas/TelegramLink"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"$ref":"#/components/responses/ServiceUnavailable"}}}},"/v1/slack":{"get":{"operationId":"getSlack","tags":["Delivery"],"summary":"Slack status","description":"Whether this server has a Slack app, and whether the member's Slack DM is connected (and in which Slack workspace).","security":[{"session":[]}],"x-logu-access":{"human":"account","agent":false},"responses":{"200":{"description":"The member's Slack status.","headers":{"Logu-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"example":{"available":true,"connected":true,"team":"Acme"},"schema":{"$ref":"#/components/schemas/SlackStatus"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"500":{"$ref":"#/components/responses/InternalError"}}},"delete":{"operationId":"disconnectSlack","tags":["Delivery"],"summary":"Disconnect Slack","description":"Stops delivery to the member's Slack DM.","security":[{"session":[]}],"x-logu-access":{"human":"account","agent":false},"responses":{"204":{"description":"Disconnected.","headers":{"Logu-Request-Id":{"$ref":"#/components/headers/RequestId"}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/v1/slack/install":{"post":{"operationId":"createSlackInstall","tags":["Delivery"],"summary":"Connect Slack","description":"Slack's \"Add to Slack\" page (OAuth v2) with a one-time state, valid 10 minutes: approving it there connects the member's DM with the Logu app, where asks and events then arrive.","security":[{"session":[]}],"x-logu-access":{"human":"account","agent":false},"responses":{"201":{"description":"The page to open and when its state expires.","headers":{"Logu-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"example":{"url":"https://slack.com/oauth/v2/authorize?client_id=1234567890.1234567890&scope=chat%3Awrite%2Cim%3Awrite&redirect_uri=https%3A%2F%2Fapi.logu.dev%2Fslack%2Foauth&state=Zk3mP9qR2sT5vW8yA1bC4dE7","expiresAt":"2026-10-12T14:13:11.000Z"},"schema":{"$ref":"#/components/schemas/SlackInstall"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"$ref":"#/components/responses/ServiceUnavailable"}}}},"/v1/whatsapp":{"get":{"operationId":"getWhatsApp","tags":["Delivery"],"summary":"WhatsApp status","description":"Whether this server has WhatsApp set up, and the member's connected WhatsApp number, if any.","security":[{"session":[]}],"x-logu-access":{"human":"account","agent":false},"responses":{"200":{"description":"The member's WhatsApp status.","headers":{"Logu-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"example":{"available":true,"connected":true,"number":"+5511987654321"},"schema":{"$ref":"#/components/schemas/WhatsAppStatus"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"500":{"$ref":"#/components/responses/InternalError"}}},"delete":{"operationId":"disconnectWhatsApp","tags":["Delivery"],"summary":"Disconnect WhatsApp","description":"Stops delivery to the member's WhatsApp number.","security":[{"session":[]}],"x-logu-access":{"human":"account","agent":false},"responses":{"204":{"description":"Disconnected.","headers":{"Logu-Request-Id":{"$ref":"#/components/headers/RequestId"}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/v1/whatsapp/link":{"post":{"operationId":"createWhatsAppLink","tags":["Delivery"],"summary":"Connect WhatsApp","description":"A one-time wa.me link: it opens WhatsApp with a message to Logu already typed, and sending it connects that number to the member (valid 10 minutes).","security":[{"session":[]}],"x-logu-access":{"human":"account","agent":false},"responses":{"201":{"description":"The link and when it expires.","headers":{"Logu-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"example":{"url":"https://wa.me/5511912345678?text=Connect%20Logu%20Zk3mP9qR2sT5vW8yA1bC4dE7fG0hJ3kL","expiresAt":"2026-10-12T14:13:11.000Z"},"schema":{"$ref":"#/components/schemas/WhatsAppLink"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"$ref":"#/components/responses/ServiceUnavailable"}}}},"/v1/delivery/test":{"post":{"operationId":"testDelivery","tags":["Delivery"],"summary":"Send a test","description":"Sends the signed-in member a test through one of their own deliveries (`push`, `browser`, `email`, `telegram`, `slack` or `whatsapp`): \"Logu test · this is how an ask will look\", rendered like a real ask with Approve and Decline that do nothing. It is not an ask (nothing is created or decidable) and it ignores quiet hours; `quietHoursIgnored` says so when a real ask would have been held. `result` is `sent`, `not_connected` (nothing is set up for it), `unavailable` (this server doesn't send it) or `failed`. Humans only, up to 5 per minute. Records `delivery.tested`.","security":[{"session":[]}],"x-logu-access":{"human":"account","agent":false},"requestBody":{"required":true,"content":{"application/json":{"example":{"delivery":"telegram"},"schema":{"type":"object","properties":{"delivery":{"type":"string","enum":["push","browser","email","telegram","slack","whatsapp"]}},"required":["delivery"]}}}},"responses":{"200":{"description":"What the test attempted.","headers":{"Logu-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"example":{"delivery":"telegram","result":"sent","count":1,"quietHoursIgnored":false},"schema":{"type":"object","properties":{"delivery":{"type":"string","enum":["push","browser","email","telegram","slack","whatsapp"]},"result":{"type":"string","enum":["sent","not_connected","unavailable","failed"]},"count":{"type":"integer","minimum":0},"quietHoursIgnored":{"type":"boolean"}},"required":["delivery","result","count","quietHoursIgnored"]}}}},"400":{"$ref":"#/components/responses/BadRequestOrUnknownField"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/v1/preferences":{"get":{"operationId":"getPreferences","tags":["Delivery"],"summary":"Delivery preferences","description":"The member's delivery settings, the same in every workspace: email for asks, quiet hours, whether quiet hours hold asks too, and the language of notifications and emails.","security":[{"session":[]}],"x-logu-access":{"human":"account","agent":false},"responses":{"200":{"description":"The preferences.","headers":{"Logu-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"example":{"emailAsks":true,"quietHours":{"start":"22:00","end":"07:00","timeZone":"America/Sao_Paulo"},"quietAsks":false,"locale":"pt-BR","emailAvailable":true},"schema":{"$ref":"#/components/schemas/Preferences"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"500":{"$ref":"#/components/responses/InternalError"}}},"patch":{"operationId":"updatePreferences","tags":["Delivery"],"summary":"Change delivery preferences","description":"Send only what changes. Quiet hours are a daily window in the member's time zone when deliveries are held (events always; asks only with `quietAsks`); `null` turns them off. `locale` (`en`, `pt-BR`) sets the language of the member's notifications and emails.","security":[{"session":[]}],"x-logu-access":{"human":"account","agent":false},"requestBody":{"required":true,"content":{"application/json":{"example":{"quietHours":{"start":"22:00","end":"07:00","timeZone":"America/Sao_Paulo"}},"schema":{"$ref":"#/components/schemas/UpdatePreferencesInput"}}}},"responses":{"200":{"description":"The preferences.","headers":{"Logu-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"example":{"emailAsks":true,"quietHours":{"start":"22:00","end":"07:00","timeZone":"America/Sao_Paulo"},"quietAsks":false,"locale":"pt-BR","emailAvailable":true},"schema":{"$ref":"#/components/schemas/Preferences"}}}},"400":{"$ref":"#/components/responses/BadRequestOrUnknownField"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/v1/stream":{"get":{"operationId":"stream","tags":["Events"],"summary":"Realtime stream (WebSocket)","description":"The apps' realtime feed: a WebSocket upgrade that pushes `event.created`, `ask.created` and `ask.settled` messages for the workspace. Browsers can't send headers on a WebSocket, so the workspace may also be given as `?workspace=`. Answers 101 Switching Protocols. A workspace holds at most 200 streams: over that, the socket opens and is closed with code 1013 (try again later), so reconnect with backoff and jitter. The text frame `resync` (not JSON) means messages were dropped under load: reload your lists. Answers 503 `overloaded` with `Retry-After` when the hub can't take the connection.","security":[{"session":[]}],"x-logu-access":{"human":"viewer","agent":false},"parameters":[{"schema":{"type":"string","example":"wsp_01K7D7Y8Z0B2D4F6H8K0M2P4R6"},"required":false,"description":"The workspace to stream (instead of the `Logu-Workspace` header).","name":"workspace","in":"query"},{"schema":{"type":"string","example":"wsp_01K7D7Y8Z0B2D4F6H8K0M2P4R6"},"required":false,"description":"Humans only: the workspace to act in (default: your personal workspace). Agents always act in their API key's workspace.","name":"Logu-Workspace","in":"header"}],"responses":{"101":{"description":"Switching Protocols: the WebSocket is open.","headers":{"Logu-Request-Id":{"$ref":"#/components/headers/RequestId"}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"$ref":"#/components/responses/Overloaded"}}}},"/v1/workspaces":{"get":{"operationId":"listWorkspaces","tags":["Workspaces"],"summary":"List your workspaces","description":"Every workspace the member belongs to, with their role. The personal one is created at sign-up and can't be deleted.","security":[{"session":[]}],"x-logu-access":{"human":"account","agent":false},"responses":{"200":{"description":"Workspaces.","headers":{"Logu-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"example":{"workspaces":[{"id":"wsp_Xq3bY7tLmN2pR8sV","name":"Ana","personal":true,"role":"owner","plan":"free","createdAt":"2026-10-01T08:55:00.000Z"},{"id":"wsp_01K7D7Y8Z0B2D4F6H8K0M2P4R6","name":"Acme ops","personal":false,"role":"owner","plan":"pro","createdAt":"2026-10-02T12:00:00.000Z"}]},"schema":{"type":"object","properties":{"workspaces":{"type":"array","items":{"$ref":"#/components/schemas/Workspace"}}},"required":["workspaces"]}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"500":{"$ref":"#/components/responses/InternalError"}}},"post":{"operationId":"createWorkspace","tags":["Workspaces"],"summary":"Create a workspace","description":"A shared workspace; its creator is its first Owner. Humans only, and limited per account whatever the plan: a Free account owns up to 3 shared workspaces (25 once it owns one on Pro; the personal workspace doesn't count), creates at most 5 a day, and needs a verified email. Accounts above the limit keep what they own. 402 `plan_limit` (a Free account at its 3) or `ceiling_reached` (at the most an account may own), 429 `rate_limited` (5 a day; `Retry-After` runs to 00:00 UTC), 403 `email_not_verified`. Nothing is created in any of them.","security":[{"session":[]}],"x-logu-access":{"human":"account","agent":false},"requestBody":{"required":true,"content":{"application/json":{"example":{"name":"Acme ops"},"schema":{"$ref":"#/components/schemas/CreateWorkspaceInput"}}}},"responses":{"201":{"description":"The workspace.","headers":{"Logu-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"example":{"id":"wsp_01K7D7Y8Z0B2D4F6H8K0M2P4R6","name":"Acme ops","personal":false,"role":"owner","plan":"pro","createdAt":"2026-10-02T12:00:00.000Z"},"schema":{"$ref":"#/components/schemas/Workspace"}}}},"400":{"$ref":"#/components/responses/BadRequestOrUnknownField"},"401":{"$ref":"#/components/responses/Unauthorized"},"402":{"$ref":"#/components/responses/PlanLimitOrCeilingReached"},"403":{"$ref":"#/components/responses/ForbiddenOrEmailNotVerified"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/v1/workspaces/{id}":{"patch":{"operationId":"renameWorkspace","tags":["Workspaces"],"summary":"Rename a workspace","description":"Owners only.","security":[{"session":[]}],"x-logu-access":{"human":"owner","agent":false},"parameters":[{"schema":{"type":"string","example":"wsp_01K7D7Y8Z0B2D4F6H8K0M2P4R6"},"required":true,"description":"The workspace's id (`wsp_…`). The request acts in this workspace (the `Logu-Workspace` header is ignored).","name":"id","in":"path"}],"requestBody":{"required":true,"content":{"application/json":{"example":{"name":"Acme ops"},"schema":{"$ref":"#/components/schemas/UpdateWorkspaceInput"}}}},"responses":{"200":{"description":"The workspace.","headers":{"Logu-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"example":{"id":"wsp_01K7D7Y8Z0B2D4F6H8K0M2P4R6","name":"Acme ops","personal":false,"role":"owner","plan":"pro","createdAt":"2026-10-02T12:00:00.000Z"},"schema":{"$ref":"#/components/schemas/Workspace"}}}},"400":{"$ref":"#/components/responses/BadRequestOrUnknownField"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"500":{"$ref":"#/components/responses/InternalError"}}},"delete":{"operationId":"deleteWorkspace","tags":["Workspaces"],"summary":"Delete a workspace","description":"Owners only. Deletes the shared workspace and everything in it: asks, events, agents and their API keys, metrics, the activity log. A personal workspace can't be deleted (400).","security":[{"session":[]}],"x-logu-access":{"human":"owner","agent":false},"parameters":[{"schema":{"type":"string","example":"wsp_01K7D7Y8Z0B2D4F6H8K0M2P4R6"},"required":true,"description":"The workspace's id (`wsp_…`). The request acts in this workspace (the `Logu-Workspace` header is ignored).","name":"id","in":"path"}],"responses":{"204":{"description":"Deleted.","headers":{"Logu-Request-Id":{"$ref":"#/components/headers/RequestId"}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/v1/workspaces/{id}/members":{"get":{"operationId":"listMembers","tags":["Members"],"summary":"List members","description":"The workspace's members and their roles.","security":[{"session":[]}],"x-logu-access":{"human":"viewer","agent":false},"parameters":[{"schema":{"type":"string","example":"wsp_01K7D7Y8Z0B2D4F6H8K0M2P4R6"},"required":true,"description":"The workspace's id (`wsp_…`). The request acts in this workspace (the `Logu-Workspace` header is ignored).","name":"id","in":"path"}],"responses":{"200":{"description":"Members.","headers":{"Logu-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"example":{"members":[{"id":"Xq3bY7tLmN2pR8sV","name":"Ana","email":"ana@example.com","role":"owner","joinedAt":"2026-10-02T12:00:00.000Z"},{"id":"Hk9cW2vZpT4mQ6nJ","name":"Bruno","email":"bruno@example.com","role":"decider","joinedAt":"2026-10-03T10:00:00.000Z"}]},"schema":{"type":"object","properties":{"members":{"type":"array","items":{"$ref":"#/components/schemas/Member"}}},"required":["members"]}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/v1/workspaces/{id}/members/{userId}":{"patch":{"operationId":"changeRole","tags":["Members"],"summary":"Change a member's role","description":"Owners only. Roles: `owner` (everything), `decider` (decides asks; manages agents, projects and threshold alerts), `viewer` (sees everything, decides nothing). The last Owner can't be demoted (409). Making someone an Owner counts for that person like creating a workspace does: they need a verified email (403 `email_not_verified`) and must own fewer shared workspaces than an account may (402 `plan_limit` or `ceiling_reached`).","security":[{"session":[]}],"x-logu-access":{"human":"owner","agent":false},"parameters":[{"schema":{"type":"string","example":"wsp_01K7D7Y8Z0B2D4F6H8K0M2P4R6"},"required":true,"description":"The workspace's id (`wsp_…`). The request acts in this workspace (the `Logu-Workspace` header is ignored).","name":"id","in":"path"},{"schema":{"type":"string","example":"Hk9cW2vZpT4mQ6nJ"},"required":true,"description":"The member's user id.","name":"userId","in":"path"}],"requestBody":{"required":true,"content":{"application/json":{"example":{"role":"decider"},"schema":{"$ref":"#/components/schemas/UpdateMemberInput"}}}},"responses":{"200":{"description":"The member.","headers":{"Logu-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"example":{"id":"Hk9cW2vZpT4mQ6nJ","name":"Bruno","email":"bruno@example.com","role":"decider","joinedAt":"2026-10-03T10:00:00.000Z"},"schema":{"$ref":"#/components/schemas/Member"}}}},"400":{"$ref":"#/components/responses/BadRequestOrUnknownField"},"401":{"$ref":"#/components/responses/Unauthorized"},"402":{"$ref":"#/components/responses/PlanLimitOrCeilingReached"},"403":{"$ref":"#/components/responses/ForbiddenOrEmailNotVerified"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"500":{"$ref":"#/components/responses/InternalError"}}},"delete":{"operationId":"removeMember","tags":["Members"],"summary":"Remove a member, or leave","description":"Owners remove anyone; any member can remove themselves (leave). The last Owner can't (409).","security":[{"session":[]}],"x-logu-access":{"human":"viewer","agent":false},"parameters":[{"schema":{"type":"string","example":"wsp_01K7D7Y8Z0B2D4F6H8K0M2P4R6"},"required":true,"description":"The workspace's id (`wsp_…`). The request acts in this workspace (the `Logu-Workspace` header is ignored).","name":"id","in":"path"},{"schema":{"type":"string","example":"Hk9cW2vZpT4mQ6nJ"},"required":true,"description":"The member's user id.","name":"userId","in":"path"}],"responses":{"204":{"description":"Removed.","headers":{"Logu-Request-Id":{"$ref":"#/components/headers/RequestId"}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/v1/workspaces/{id}/invitations":{"get":{"operationId":"listInvitations","tags":["Invitations"],"summary":"List invitations","description":"Owners only. The workspace's invitations, pending ones first.","security":[{"session":[]}],"x-logu-access":{"human":"owner","agent":false},"parameters":[{"schema":{"type":"string","example":"wsp_01K7D7Y8Z0B2D4F6H8K0M2P4R6"},"required":true,"description":"The workspace's id (`wsp_…`). The request acts in this workspace (the `Logu-Workspace` header is ignored).","name":"id","in":"path"}],"responses":{"200":{"description":"Invitations.","headers":{"Logu-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"example":{"invitations":[{"id":"inv_01K7DB4D6F8H0K2M4P6R8T0V2X","email":"carla@example.com","role":"viewer","status":"pending","invitedBy":{"id":"Xq3bY7tLmN2pR8sV","name":"Ana"},"expiresAt":"2026-10-19T14:03:11.000Z","createdAt":"2026-10-12T14:03:11.000Z"}]},"schema":{"type":"object","properties":{"invitations":{"type":"array","items":{"$ref":"#/components/schemas/Invitation"}}},"required":["invitations"]}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"500":{"$ref":"#/components/responses/InternalError"}}},"post":{"operationId":"createInvitation","tags":["Invitations"],"summary":"Invite someone","description":"Owners only. Invites an email address with a role: a one-time link, valid 7 days, emailed and also returned (to share it another way when email is off). Accepting requires signing in with that email. One pending invitation per email per workspace.","security":[{"session":[]}],"x-logu-access":{"human":"owner","agent":false},"parameters":[{"schema":{"type":"string","example":"wsp_01K7D7Y8Z0B2D4F6H8K0M2P4R6"},"required":true,"description":"The workspace's id (`wsp_…`). The request acts in this workspace (the `Logu-Workspace` header is ignored).","name":"id","in":"path"}],"requestBody":{"required":true,"content":{"application/json":{"example":{"email":"carla@example.com","role":"viewer"},"schema":{"$ref":"#/components/schemas/CreateInvitationInput"}}}},"responses":{"201":{"description":"The invitation, its link, and whether the email went out.","headers":{"Logu-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"example":{"invitation":{"id":"inv_01K7DB4D6F8H0K2M4P6R8T0V2X","email":"carla@example.com","role":"viewer","status":"pending","invitedBy":{"id":"Xq3bY7tLmN2pR8sV","name":"Ana"},"expiresAt":"2026-10-19T14:03:11.000Z","createdAt":"2026-10-12T14:03:11.000Z"},"url":"https://logu.dev/invite/Zk3mP9qR2sT5vW8yA1bC4dE7fG0hJ3kL","emailSent":true},"schema":{"$ref":"#/components/schemas/InvitationSent"}}}},"400":{"$ref":"#/components/responses/BadRequestOrUnknownField"},"401":{"$ref":"#/components/responses/Unauthorized"},"402":{"$ref":"#/components/responses/PlanLimit"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/v1/workspaces/{id}/invitations/{invitationId}/resend":{"post":{"operationId":"resendInvitation","tags":["Invitations"],"summary":"Resend an invitation","description":"Owners only. A fresh link for a pending invitation (the old one stops working).","security":[{"session":[]}],"x-logu-access":{"human":"owner","agent":false},"parameters":[{"schema":{"type":"string","example":"wsp_01K7D7Y8Z0B2D4F6H8K0M2P4R6"},"required":true,"description":"The workspace's id (`wsp_…`). The request acts in this workspace (the `Logu-Workspace` header is ignored).","name":"id","in":"path"},{"schema":{"type":"string","example":"inv_01K7DB4D6F8H0K2M4P6R8T0V2X"},"required":true,"description":"The invitation's id (`inv_…`).","name":"invitationId","in":"path"}],"responses":{"200":{"description":"The invitation and its new link.","headers":{"Logu-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"example":{"invitation":{"id":"inv_01K7DB4D6F8H0K2M4P6R8T0V2X","email":"carla@example.com","role":"viewer","status":"pending","invitedBy":{"id":"Xq3bY7tLmN2pR8sV","name":"Ana"},"expiresAt":"2026-10-19T14:03:11.000Z","createdAt":"2026-10-12T14:03:11.000Z"},"url":"https://logu.dev/invite/Zk3mP9qR2sT5vW8yA1bC4dE7fG0hJ3kL","emailSent":true},"schema":{"$ref":"#/components/schemas/InvitationSent"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"402":{"$ref":"#/components/responses/PlanLimit"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/v1/workspaces/{id}/invitations/{invitationId}":{"delete":{"operationId":"revokeInvitation","tags":["Invitations"],"summary":"Revoke an invitation","description":"Owners only. The link stops working.","security":[{"session":[]}],"x-logu-access":{"human":"owner","agent":false},"parameters":[{"schema":{"type":"string","example":"wsp_01K7D7Y8Z0B2D4F6H8K0M2P4R6"},"required":true,"description":"The workspace's id (`wsp_…`). The request acts in this workspace (the `Logu-Workspace` header is ignored).","name":"id","in":"path"},{"schema":{"type":"string","example":"inv_01K7DB4D6F8H0K2M4P6R8T0V2X"},"required":true,"description":"The invitation's id (`inv_…`).","name":"invitationId","in":"path"}],"responses":{"204":{"description":"Revoked.","headers":{"Logu-Request-Id":{"$ref":"#/components/headers/RequestId"}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/v1/invitations/accept":{"post":{"operationId":"acceptInvitation","tags":["Invitations"],"summary":"Accept an invitation","description":"The signed-in human joins the workspace with the invited role; their email must be the invited one, and verified (a magic-link sign-in or the confirmation link verifies it: `POST /api/auth/verify-email` sends one). An unverified email is a 403 `email_not_verified`. An invitation as Owner also needs the account to own fewer shared workspaces than an account may (402; the invitation stays pending).","security":[{"session":[]}],"x-logu-access":{"human":"account","agent":false},"requestBody":{"required":true,"content":{"application/json":{"example":{"token":"Zk3mP9qR2sT5vW8yA1bC4dE7fG0hJ3kL"},"schema":{"$ref":"#/components/schemas/AcceptInvitationInput"}}}},"responses":{"200":{"description":"The workspace joined.","headers":{"Logu-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"example":{"workspace":{"id":"wsp_01K7D7Y8Z0B2D4F6H8K0M2P4R6","name":"Acme ops","personal":false,"role":"viewer","plan":"pro","createdAt":"2026-10-02T12:00:00.000Z"}},"schema":{"type":"object","properties":{"workspace":{"$ref":"#/components/schemas/Workspace"}},"required":["workspace"]}}}},"400":{"$ref":"#/components/responses/BadRequestOrUnknownField"},"401":{"$ref":"#/components/responses/Unauthorized"},"402":{"$ref":"#/components/responses/PlanLimitOrCeilingReached"},"403":{"$ref":"#/components/responses/ForbiddenOrEmailNotVerified"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/llms.txt":{"get":{"tags":["Meta"],"security":[],"operationId":"llmsTxt","summary":"Agent guide (llms.txt)","description":"The short guide for agents (llmstxt.org).","x-agent-hint":"Read first if you've never used Logu.","responses":{"200":{"description":"The guide, Markdown.","headers":{"Logu-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"text/plain":{"schema":{"type":"string"}}}}}}},"/llms-full.txt":{"get":{"tags":["Meta"],"security":[],"operationId":"llmsFullTxt","summary":"Full agent guide","description":"llms.txt plus the agent guide (docs/agents.md) and every error code.","responses":{"200":{"description":"The full guide, Markdown.","headers":{"Logu-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"text/plain":{"schema":{"type":"string"}}}}}}},"/openapi.json":{"get":{"tags":["Meta"],"security":[],"operationId":"openapi","summary":"This OpenAPI document","description":"The OpenAPI 3.1 contract of the API, generated from the routes that serve it.","responses":{"200":{"description":"The document.","headers":{"Logu-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"type":"object","properties":{},"additionalProperties":{}}}}}}}},"/docs":{"get":{"tags":["Meta"],"security":[],"operationId":"docs","summary":"API reference","description":"This document as a readable page.","responses":{"200":{"description":"The reference.","headers":{"Logu-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"text/html":{"schema":{"type":"string"}}}}}}},"/mcp":{"post":{"operationId":"mcp","tags":["Meta"],"summary":"MCP server (Streamable HTTP)","description":"Model Context Protocol over Streamable HTTP (JSON-RPC), authenticated with an API key. Tools: `ask_human`, `get_decision`, `withdraw_ask`, `list_asks`, `send_event`, `report_metric`, `whoami`.","security":[{"apiKey":[]}],"x-agent-hint":"If your runtime speaks MCP, add this server instead of calling the HTTP API: `claude mcp add --transport http logu https://api.logu.dev/mcp --header \"Authorization: Bearer $LOGU_API_KEY\"`. ask_human waits up to wait_seconds (max 50); call get_decision again while it's waiting.","requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{},"additionalProperties":{}}}}},"responses":{"200":{"description":"A JSON-RPC response (or an event stream).","headers":{"Logu-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"type":"object","properties":{},"additionalProperties":{}}},"text/event-stream":{"schema":{"type":"string"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/a/{id}":{"get":{"operationId":"signedAttachment","tags":["Attachments"],"summary":"Image by signed link","description":"An attachment's image without credentials, for `<img>` tags, notifications and Telegram: the `url` of each attachment in an ask or event. The signature covers the id and the expiry; the link works for at least an hour, then answers 403 (read the ask or event again for a fresh one).","security":[],"parameters":[{"schema":{"type":"string","example":"att_01K7DQ6C8E0G2J4L6N8Q0S2U4W"},"required":true,"description":"The attachment's id (`att_…`).","name":"id","in":"path"},{"schema":{"type":"string","example":"1791824400"},"required":true,"description":"When the link expires (Unix seconds).","name":"exp","in":"query"},{"schema":{"type":"string","example":"q8Hk2mP0vX3nR5tY7wZ9bD1fG3jL5nQ7sU9wY1aC3eE"},"required":true,"description":"The link's signature.","name":"sig","in":"query"}],"responses":{"200":{"description":"The image (inline, never sniffed, cached privately until the link expires).","headers":{"Logu-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"image/png":{"schema":{"type":"string","format":"binary"}},"image/jpeg":{"schema":{"type":"string","format":"binary"}},"image/webp":{"schema":{"type":"string","format":"binary"}},"image/gif":{"schema":{"type":"string","format":"binary"}}}},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"}}}},"/r/{token}":{"get":{"operationId":"decisionLinkPage","tags":["Delivery"],"summary":"Decision link: confirm","description":"Opened from an email or a browser notification: shows the ask and one button for the link's option. GET never decides (link previews and email scanners are safe). No session needed: the signed link is the authority, for a Decider or Owner of the ask's workspace.","security":[],"parameters":[{"schema":{"type":"string","example":"eyJhIjoiYXNrXzAxSzdEUTNNMlY4SDRONlI5VDFXM1k1QjdDIiwibyI6ImFwcHJvdmUifQ.c2ln"},"required":true,"description":"The signed decision link token (from an email or a notification).","name":"token","in":"path"}],"responses":{"200":{"description":"The confirmation page.","headers":{"Logu-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"text/html":{"schema":{"type":"string"}}}},"400":{"description":"The link is invalid or expired.","headers":{"Logu-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"text/html":{"schema":{"type":"string"}}}},"403":{"description":"The member may no longer decide in this workspace.","headers":{"Logu-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"text/html":{"schema":{"type":"string"}}}},"409":{"description":"The ask already settled.","headers":{"Logu-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"text/html":{"schema":{"type":"string"}}}}}},"post":{"operationId":"decisionLinkDecide","tags":["Delivery"],"summary":"Decision link: decide","description":"The confirmation page's button: decides the ask with the link's option (`decidedVia: \"link\"`).","security":[],"parameters":[{"schema":{"type":"string","example":"eyJhIjoiYXNrXzAxSzdEUTNNMlY4SDRONlI5VDFXM1k1QjdDIiwibyI6ImFwcHJvdmUifQ.c2ln"},"required":true,"description":"The signed decision link token (from an email or a notification).","name":"token","in":"path"}],"responses":{"200":{"description":"Decided.","headers":{"Logu-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"text/html":{"schema":{"type":"string"}}}},"400":{"description":"The link is invalid or expired.","headers":{"Logu-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"text/html":{"schema":{"type":"string"}}}},"403":{"description":"The member may no longer decide in this workspace.","headers":{"Logu-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"text/html":{"schema":{"type":"string"}}}},"409":{"description":"The ask already settled.","headers":{"Logu-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"text/html":{"schema":{"type":"string"}}}}}}},"/api/auth/magic-link":{"post":{"operationId":"requestMagicLink","tags":["Sign-in"],"summary":"Email a magic link","description":"Emails a one-time link (15 minutes) to sign in, create the account, or reset the password. The answer is the same whether or not the address has an account. Rate limited per IP and per email. When the server uses Cloudflare Turnstile the app sends its token in the `Turnstile-Token` header (403 `forbidden` without a valid one). For an address with no account, or with an unconfirmed email, a global daily cap applies (429 `rate_limited`, calm text, `Retry-After` to 00:00 UTC); an address that exists with a confirmed email is never capped.","security":[],"requestBody":{"required":true,"content":{"application/json":{"example":{"email":"ana@example.com","intent":"sign-in","locale":"en"},"schema":{"$ref":"#/components/schemas/MagicLinkRequestInput"}}}},"responses":{"200":{"description":"Sent (or would have been).","headers":{"Logu-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"example":{"ok":true},"schema":{"type":"object","properties":{"ok":{"type":"boolean","enum":[true]}},"required":["ok"]}}}},"400":{"$ref":"#/components/responses/UnknownFieldOrBadRequest"},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"$ref":"#/components/responses/ServiceUnavailable"}}}},"/api/auth/magic-link/peek":{"post":{"operationId":"peekMagicLink","tags":["Sign-in"],"summary":"See what a magic link does","description":"The account a link signs in to, without spending it: the app shows it before the click (opening a link never signs in). `removesSignInMethods` warns that an unverified account loses the password and passkeys set before its email was verified. An account in its 30-day deletion grace only gets `restore` links (`intent: \"restore\"`, `deletionPurgeAt`): clicking one cancels the deletion.","security":[],"requestBody":{"required":true,"content":{"application/json":{"example":{"token":"q9XvT2mLp8RkW4nB6cYz1dHf3jS5aU7e"},"schema":{"$ref":"#/components/schemas/MagicLinkTokenInput"}}}},"responses":{"200":{"description":"What the link does.","headers":{"Logu-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"example":{"email":"ana@example.com","intent":"sign-in","newAccount":false,"removesSignInMethods":false,"deletionPurgeAt":null},"schema":{"$ref":"#/components/schemas/MagicLinkPeek"}}}},"400":{"$ref":"#/components/responses/UnknownFieldOrBadRequest"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/api/auth/magic-link/verify":{"post":{"operationId":"verifyMagicLink","tags":["Sign-in"],"summary":"Sign in with a magic link","description":"Spends the link (once) and signs in, creating the account when there is none; pending invitations for the email are accepted. The session token is in `set-auth-token`.","security":[],"requestBody":{"required":true,"content":{"application/json":{"example":{"token":"q9XvT2mLp8RkW4nB6cYz1dHf3jS5aU7e"},"schema":{"$ref":"#/components/schemas/MagicLinkTokenInput"}}}},"responses":{"200":{"description":"Signed in.","headers":{"Logu-Request-Id":{"$ref":"#/components/headers/RequestId"},"set-auth-token":{"description":"The new session token: send it as `Authorization: Bearer <token>` to `/v1` (scheme `session`).","schema":{"type":"string","example":"Ln3Vq7…signed"}}},"content":{"application/json":{"example":{"intent":"sign-in","newAccount":false,"passwordRemoved":false,"joined":[]},"schema":{"$ref":"#/components/schemas/MagicLinkVerified"}}}},"400":{"$ref":"#/components/responses/UnknownFieldOrBadRequest"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/api/auth/verify-email":{"post":{"operationId":"sendEmailConfirmation","tags":["Sign-in"],"summary":"Email a link to confirm your email","description":"Sends the signed-in human a link that verifies their email (needed to accept invitations). It keeps their password and passkeys.","security":[{"session":[]}],"requestBody":{"required":true,"content":{"application/json":{"example":{"locale":"en"},"schema":{"type":"object","properties":{"locale":{"type":"string","enum":["en","pt-BR"],"default":"en"}},"additionalProperties":false}}}},"responses":{"200":{"description":"Sent, or already verified.","headers":{"Logu-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"example":{"ok":true,"email":"ana@example.com","alreadyVerified":false},"schema":{"type":"object","properties":{"ok":{"type":"boolean","enum":[true]},"email":{"type":"string"},"alreadyVerified":{"type":"boolean"}},"required":["ok","email","alreadyVerified"]}}}},"400":{"$ref":"#/components/responses/UnknownFieldOrBadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"$ref":"#/components/responses/ServiceUnavailable"}}}},"/api/auth/passkeys/sign-in/options":{"post":{"operationId":"passkeySignInOptions","tags":["Sign-in"],"summary":"Start a passkey sign-in","description":"WebAuthn options for discoverable credentials (user verification required).","security":[],"responses":{"200":{"description":"The ceremony.","headers":{"Logu-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"example":{"options":{"challenge":"Zk3vQ9…","rpId":"logu.dev","userVerification":"required"},"challengeToken":"aS7dQ2xLp9Vt4mKz.signature"},"schema":{"type":"object","properties":{"options":{"type":"object","additionalProperties":{},"description":"`PublicKeyCredentialCreationOptionsJSON` / `…RequestOptionsJSON`."},"challengeToken":{"type":"string","description":"The ceremony's signed challenge reference: send it back with the response."}},"required":["options","challengeToken"]}}}},"500":{"$ref":"#/components/responses/InternalError"}}}},"/api/auth/passkeys/sign-in":{"post":{"operationId":"passkeySignIn","tags":["Sign-in"],"summary":"Sign in with a passkey","description":"Verifies the authenticator's response; the session token is in `set-auth-token`.","security":[],"requestBody":{"required":true,"content":{"application/json":{"example":{"response":{"id":"cred"},"challengeToken":"aS7dQ2xLp9Vt4mKz.signature"},"schema":{"type":"object","properties":{"response":{"type":"object","additionalProperties":{},"description":"The authenticator's response (`startRegistration` / `startAuthentication`)."},"challengeToken":{"type":"string","minLength":16,"maxLength":1000},"name":{"type":"string","description":"Registration only: a name (≤ 64)."}},"required":["response","challengeToken"],"additionalProperties":false}}}},"responses":{"200":{"description":"Signed in.","headers":{"Logu-Request-Id":{"$ref":"#/components/headers/RequestId"},"set-auth-token":{"description":"The new session token: send it as `Authorization: Bearer <token>` to `/v1` (scheme `session`).","schema":{"type":"string","example":"Ln3Vq7…signed"}}},"content":{"application/json":{"example":{"ok":true},"schema":{"type":"object","properties":{"ok":{"type":"boolean","enum":[true]}},"required":["ok"]}}}},"400":{"$ref":"#/components/responses/UnknownFieldOrBadRequest"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/api/auth/passkeys/register/options":{"post":{"operationId":"passkeyRegisterOptions","tags":["Sign-in"],"summary":"Start adding a passkey","description":"WebAuthn creation options for the signed-in account.","security":[{"session":[]}],"responses":{"200":{"description":"The ceremony.","headers":{"Logu-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"example":{"options":{"challenge":"Zk3vQ9…","rpId":"logu.dev","userVerification":"required"},"challengeToken":"aS7dQ2xLp9Vt4mKz.signature"},"schema":{"type":"object","properties":{"options":{"type":"object","additionalProperties":{},"description":"`PublicKeyCredentialCreationOptionsJSON` / `…RequestOptionsJSON`."},"challengeToken":{"type":"string","description":"The ceremony's signed challenge reference: send it back with the response."}},"required":["options","challengeToken"]}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/api/auth/passkeys/register":{"post":{"operationId":"registerPasskey","tags":["Sign-in"],"summary":"Add a passkey","description":"Verifies the authenticator's response (user verification required) and saves the passkey. Needs a sign-in from the last 24 hours (`forbidden` otherwise).","security":[{"session":[]}],"requestBody":{"required":true,"content":{"application/json":{"example":{"response":{"id":"cred"},"challengeToken":"aS7dQ2xLp9Vt4mKz.signature","name":"Chrome · macOS"},"schema":{"type":"object","properties":{"response":{"type":"object","additionalProperties":{},"description":"The authenticator's response (`startRegistration` / `startAuthentication`)."},"challengeToken":{"type":"string","minLength":16,"maxLength":1000},"name":{"type":"string","description":"Registration only: a name (≤ 64)."}},"required":["response","challengeToken"],"additionalProperties":false}}}},"responses":{"201":{"description":"The passkey.","headers":{"Logu-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"example":{"id":"pk_9f2c1e","name":"Chrome · macOS","provider":"Google Password Manager","backedUp":true,"createdAt":"2026-10-12T14:03:11.000Z"},"schema":{"$ref":"#/components/schemas/PasskeyInfo"}}}},"400":{"$ref":"#/components/responses/UnknownFieldOrBadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/api/auth/passkeys":{"get":{"operationId":"listPasskeys","tags":["Sign-in"],"summary":"List your passkeys","description":"The signed-in human's passkeys (never their keys).","security":[{"session":[]}],"responses":{"200":{"description":"The passkeys.","headers":{"Logu-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"example":{"passkeys":[{"id":"pk_9f2c1e","name":"Chrome · macOS","provider":"Google Password Manager","backedUp":true,"createdAt":"2026-10-12T14:03:11.000Z"}]},"schema":{"type":"object","properties":{"passkeys":{"type":"array","items":{"$ref":"#/components/schemas/PasskeyInfo"}}},"required":["passkeys"]}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/api/auth/passkeys/{id}/rename":{"post":{"operationId":"renamePasskey","tags":["Sign-in"],"summary":"Rename a passkey","description":"One line, at most 64 characters.","security":[{"session":[]}],"parameters":[{"schema":{"type":"string","example":"Xq3bY7tLmN2pR8sV"},"required":true,"description":"The passkey's or session's id.","name":"id","in":"path"}],"requestBody":{"required":true,"content":{"application/json":{"example":{"name":"Work Mac"},"schema":{"type":"object","properties":{"name":{"type":"string"}},"required":["name"],"additionalProperties":false}}}},"responses":{"200":{"description":"The passkey.","headers":{"Logu-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"example":{"id":"pk_9f2c1e","name":"Chrome · macOS","provider":"Google Password Manager","backedUp":true,"createdAt":"2026-10-12T14:03:11.000Z"},"schema":{"$ref":"#/components/schemas/PasskeyInfo"}}}},"400":{"$ref":"#/components/responses/UnknownFieldOrBadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/api/auth/passkeys/{id}/delete":{"post":{"operationId":"deletePasskey","tags":["Sign-in"],"summary":"Remove a passkey","description":"It can't sign in anymore. Recorded in the activity log.","security":[{"session":[]}],"parameters":[{"schema":{"type":"string","example":"Xq3bY7tLmN2pR8sV"},"required":true,"description":"The passkey's or session's id.","name":"id","in":"path"}],"responses":{"204":{"description":"Removed.","headers":{"Logu-Request-Id":{"$ref":"#/components/headers/RequestId"}}},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/api/auth/methods":{"get":{"operationId":"signInMethods","tags":["Sign-in"],"summary":"How you sign in","description":"Email (and whether it's verified), password, passkeys, and whether this session is fresh.","security":[{"session":[]}],"responses":{"200":{"description":"The methods.","headers":{"Logu-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"example":{"email":"ana@example.com","emailVerified":true,"password":true,"passkeys":[{"id":"pk_9f2c1e","name":"Chrome · macOS","provider":"Google Password Manager","backedUp":true,"createdAt":"2026-10-12T14:03:11.000Z"}],"freshSession":false},"schema":{"$ref":"#/components/schemas/SignInMethods"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/api/auth/password":{"post":{"operationId":"setPassword","tags":["Sign-in"],"summary":"Set or change your password","description":"Needs the current password unless this session started in the last 15 minutes (the magic-link reset path). Signs out the other sessions.","security":[{"session":[]}],"requestBody":{"required":true,"content":{"application/json":{"example":{"newPassword":"a long new password","currentPassword":"the old one"},"schema":{"$ref":"#/components/schemas/SetPasswordInput"}}}},"responses":{"200":{"description":"Saved.","headers":{"Logu-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"example":{"ok":true,"revoked":2},"schema":{"type":"object","properties":{"ok":{"type":"boolean","enum":[true]},"revoked":{"type":"integer"}},"required":["ok","revoked"]}}}},"400":{"$ref":"#/components/responses/UnknownFieldOrBadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/api/auth/sessions":{"get":{"operationId":"listSessions","tags":["Sign-in"],"summary":"List your sessions","description":"Signed-in browsers and devices: device, last seen, truncated IP. Never their tokens.","security":[{"session":[]}],"responses":{"200":{"description":"The sessions.","headers":{"Logu-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"example":{"sessions":[{"id":"ses_4b8d","device":"Chrome · macOS","createdAt":"2026-10-12T14:00:00.000Z","lastSeenAt":"2026-10-12T14:31:00.000Z","ip":"203.0.113.x","current":true}]},"schema":{"type":"object","properties":{"sessions":{"type":"array","items":{"$ref":"#/components/schemas/SessionInfo"}}},"required":["sessions"]}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/api/auth/sessions/revoke-others":{"post":{"operationId":"revokeOtherSessions","tags":["Sign-in"],"summary":"Sign out your other sessions","description":"Every session but this one.","security":[{"session":[]}],"responses":{"200":{"description":"How many were signed out.","headers":{"Logu-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"example":{"revoked":2},"schema":{"type":"object","properties":{"revoked":{"type":"integer"}},"required":["revoked"]}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/api/auth/sessions/{id}/revoke":{"post":{"operationId":"revokeSession","tags":["Sign-in"],"summary":"Sign out a session","description":"One of your sessions (this one too: that signs you out).","security":[{"session":[]}],"parameters":[{"schema":{"type":"string","example":"Xq3bY7tLmN2pR8sV"},"required":true,"description":"The passkey's or session's id.","name":"id","in":"path"}],"responses":{"204":{"description":"Signed out.","headers":{"Logu-Request-Id":{"$ref":"#/components/headers/RequestId"}}},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/api/auth/sign-up/email":{"post":{"operationId":"signUpWithPassword","tags":["Sign-in"],"summary":"Create an account with a password","description":"Better Auth's sign-up. The email isn't verified yet: Logu emails a confirmation link (needed to accept invitations). Errors are Better Auth's `{ code, message }`. The session token is in `set-auth-token`. Needs the `Turnstile-Token` header when the server uses Cloudflare Turnstile (403 `forbidden` otherwise), and a new account counts against a global daily cap (429 `rate_limited` when it is reached; accounts that exist are unaffected).","security":[],"requestBody":{"required":true,"content":{"application/json":{"example":{"name":"Ana","email":"ana@example.com","password":"correct horse battery"},"schema":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":64},"email":{"type":"string","format":"email"},"password":{"type":"string","minLength":10,"maxLength":128}},"required":["name","email","password"]}}}},"responses":{"200":{"description":"Signed up and in.","headers":{"Logu-Request-Id":{"$ref":"#/components/headers/RequestId"},"set-auth-token":{"description":"The new session token: send it as `Authorization: Bearer <token>` to `/v1` (scheme `session`).","schema":{"type":"string","example":"Ln3Vq7…signed"}}},"content":{"application/json":{"example":{"token":"Ln3Vq7","user":{"id":"Xq3bY7tLmN2pR8sV","email":"ana@example.com","name":"Ana"}},"schema":{"type":"object","properties":{"token":{"type":"string"},"user":{"type":"object","properties":{"id":{"type":"string"},"email":{"type":"string"},"name":{"type":"string"}},"required":["id","email","name"]}},"required":["token","user"]}}}},"400":{"$ref":"#/components/responses/BadRequest"},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/api/auth/sign-in/email":{"post":{"operationId":"signInWithPassword","tags":["Sign-in"],"summary":"Sign in with a password","description":"Better Auth's sign-in. Errors are Better Auth's `{ code, message }` (401 `INVALID_EMAIL_OR_PASSWORD`). Rate limited per IP. Needs the `Turnstile-Token` header when the server uses Cloudflare Turnstile (403 `forbidden` otherwise); never subject to the global daily caps.","security":[],"requestBody":{"required":true,"content":{"application/json":{"example":{"email":"ana@example.com","password":"correct horse battery"},"schema":{"type":"object","properties":{"email":{"type":"string","format":"email"},"password":{"type":"string"}},"required":["email","password"]}}}},"responses":{"200":{"description":"Signed in.","headers":{"Logu-Request-Id":{"$ref":"#/components/headers/RequestId"},"set-auth-token":{"description":"The new session token: send it as `Authorization: Bearer <token>` to `/v1` (scheme `session`).","schema":{"type":"string","example":"Ln3Vq7…signed"}}},"content":{"application/json":{"example":{"token":"Ln3Vq7","user":{"id":"Xq3bY7tLmN2pR8sV","email":"ana@example.com","name":"Ana"}},"schema":{"type":"object","properties":{"token":{"type":"string"},"user":{"type":"object","properties":{"id":{"type":"string"},"email":{"type":"string"},"name":{"type":"string"}},"required":["id","email","name"]}},"required":["token","user"]}}}},"400":{"$ref":"#/components/responses/BadRequest"},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/api/auth/sign-out":{"post":{"operationId":"signOut","tags":["Sign-in"],"summary":"Sign out","description":"Ends this session.","security":[{"session":[]}],"responses":{"200":{"description":"Signed out.","headers":{"Logu-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"example":{"success":true},"schema":{"type":"object","properties":{"success":{"type":"boolean"}},"required":["success"]}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"500":{"$ref":"#/components/responses/InternalError"}}}}},"webhooks":{}}