Limits
Every numeric limit in SAT, from field lengths and list caps to token lifetimes, leases, stream timings and runner intervals, with the values in the code.
This page lists every numeric limit the code enforces or uses, with its exact value and where it is defined. Requests that break a validation limit answer 422 with error code VALIDATION_ERROR unless noted. Money is integer cents, and a budget of 0 means no limit.
Field lengths
From the Pydantic models in apps/api/company/schemas.py and apps/api/api/schemas.py. "No limit" means the API does not cap it (the column is TEXT).
Companies
| Field | Limit |
|---|---|
name | 1 to 120 characters |
task_prefix | 1 to 8 characters, pattern ^[A-Z][A-Z0-9]*$, default SAT. Cannot be changed after creation. |
mission | No limit |
monthly_budget_cents | Integer, 0 or more |
Agents
| Field | Limit |
|---|---|
name | 1 to 100 characters |
title | Up to 120 characters on create. Not length-checked on update; the column is 120. |
role | Up to 50 characters on create, default engineer. Not length-checked on update; the column is 50. |
adapter | Up to 50 characters on create, default claude_code. Not length-checked on update; the column is 50. |
heartbeat_cron | Not validated by the API; the column is 100 characters |
instructions, capabilities | No limit |
monthly_budget_cents | Integer, 0 or more |
Work
| Field | Limit |
|---|---|
Task title | 1 to 300 characters |
Task description | No limit |
Comment body | At least 1 character, no maximum |
Goal title | 1 to 200 characters |
Project name | 1 to 100 characters |
Project repository | No API limit; the column is 500 characters |
Routine title | 1 to 200 characters |
Routine cron | 1 to 100 characters. Stored as given, not validated by the API. |
Approval new_budget_cents | Integer, 0 or more |
Runs
| Field | Limit |
|---|---|
runner_id (claim, heartbeat, report) | 1 to 100 characters. sat runner truncates its id to 100. |
tokens_in, tokens_out, cost_cents | Integers, 0 or more |
summary, error, transcript entry text | No API limit. sat runner clips them (see Runner). |
Accounts and API keys
| Field | Limit |
|---|---|
Registration password | 8 to 128 characters |
Reset password | 8 to 128 characters |
Reset token | 20 to 200 characters |
name (register, PATCH /api/me) | 1 to 255 characters |
Registration company | Up to 255 characters |
avatar_url | Up to 500 characters, must match ^https://\S+$ |
API key name | 1 to 100 characters, unique among your active (not revoked, not expired) keys |
API key expires_in_days | 1 to 3650, optional (no expiry when omitted) |
Preferences
From apps/api/api/preferences.py. Unknown fields are rejected.
| Field | Limit |
|---|---|
timezone | Up to 64 characters |
locale | Up to 35 characters, pattern ^[A-Za-z]{2,3}(-[A-Za-z0-9]{2,8})*$ |
currency | Three uppercase letters |
dashboard.range_days | 7, 14 or 30 |
dashboard.order, dashboard.hidden | Up to 20 card ids, each [a-z0-9-]{1,40} |
tasks.saved_views | Up to 20 views |
Saved view id | 1 to 40 characters, [A-Za-z0-9_-] |
Saved view name | 1 to 60 characters |
Saved view query | Up to 500 characters |
favorites.agents, favorites.projects | Up to 50 ids each |
List limits
| Endpoint | Default | Maximum | Paging |
|---|---|---|---|
GET /tasks | 500 | 1000 (limit) | None. Ordered by sort_order, then newest number first. |
GET /runs | 100 | 500 (limit) | None. Newest first. Transcripts omitted. |
GET /activity | 50 | 200 (limit) | Cursor before (the created_at of the last row seen) |
GET /dashboard active_runs | 20 | 20 | Queued or running runs |
GET /dashboard for_you.recent | 5 | 5 | Open tasks you created |
GET /dashboard days | 14 | 7, 14 or 30 only | |
| Agents, goals, projects, approvals, routines, members, companies, comments | All rows | No cap | None |
sat list commands take --limit: sat tasks list defaults to 200 rows, sat activity to 50 and sat runs list to 25.
Company and account limits
| Limit | Value | Source |
|---|---|---|
| Active API keys per user | 20 (400 API_KEY_LIMIT). Revoked and expired keys do not count | api/api_key_endpoints.py |
| Active API keys per user, legacy route | 10 | api/user_endpoints.py (unsupported) |
| Task numbers | Per company, from 1, never reused | companies.task_counter |
| Running runs per agent | 1. A claim skips agents with a running run. | POST /runs/claim |
Pending budget_override approvals per agent | 1 | services.enforce_agent_budget |
| Identical queued runs | 1 per agent, task and routine. A new wake returns the existing one. | services.queue_run |
Authentication
From apps/api/services/auth_service.py, services/password_reset.py and services/api_keys.py.
| Limit | Value |
|---|---|
| Access token lifetime | 30 minutes |
| Refresh token lifetime | 7 days |
| Failed sign-ins before lockout | 5 consecutive |
| Lockout duration | 30 minutes |
| Password reset link lifetime | 30 minutes, single use |
| Reset link cooldown | 1 per account per 60 seconds |
| Reset token entropy | 32 random bytes |
| API key secret | sat_live_ plus 32 characters |
| API key display prefix | First 13 characters |
API key last_used update | At most once per minute |
| SMTP connection timeout | 15 seconds |
Rate limits
Per client address, counted in memory in each API process.
| Endpoint | Limit |
|---|---|
GET /health | 100 per minute |
POST /api/password/forgot | 5 per minute |
POST /api/password/reset | 10 per minute |
GET /api/rate-limit-test | 10 per minute |
No other endpoint is rate limited.
Runs and leases
| Limit | Value | Source |
|---|---|---|
Lease (RUN_LEASE_SECONDS) | 300 seconds by default. A running run whose last heartbeat or report (or start) is older is failed. | company/services.py |
| Claim attempts per request | 5. If every attempt loses a race, the claim answers 204. | POST /runs/claim |
| Transcript size | No limit. The whole transcript is one JSON value in the run row. | runs.transcript |
Live events
| Limit | Value | Source |
|---|---|---|
| Stream heartbeat | : heartbeat comment after 15 seconds without an event | company/routers/insights.py |
| Reconnect hint | retry: 3000 (3 seconds), sent first | company/routers/insights.py |
| Subscriber queue | 1,000 events per connection (in-memory bus) | company/events.py |
| Client reconnect backoff | 2 seconds, doubling to a maximum of 30 seconds | libs/api-client/src/events.ts |
Runner
From apps/cli/src/runner and apps/cli/src/commands/runner.ts.
| Limit | Value |
|---|---|
| Heartbeat interval | 30 seconds |
| Transcript flush | Every 2 seconds, or sooner once 4,096 characters are buffered |
Concurrency (--concurrency) | 2 by default |
Safety-net poll (--poll) | 30 seconds by default, minimum 5 |
Simulated run length (--echo-delay) | 1,500 ms by default |
| Echo adapter usage | 100 tokens in, 50 out, 1 cent per run |
| Agent stop | SIGTERM, then SIGKILL after 5 seconds |
| Stderr kept from the agent process | Last 4,000 characters |
Run summary and error reported | 2,000 characters |
| Final comment posted to the task | 8,000 characters |
| Comments included in the task prompt | Last 20 |
| Workspace folder names | Slugs of up to 40 characters |
Transcript clipping in adapters
apps/cli/src/runner/adapters.ts. The agent's own messages are not clipped.
| Entry | Clipped to |
|---|---|
Tool call summary (→ tool input) | 200 characters |
| Claude Code tool result | 800 characters |
| Codex reasoning | 800 characters |
| Codex command output | 800 characters |
| Codex file edit summary | 300 characters |
| Unparseable output line | 500 characters |
| Agent CLI stderr in the run error | 1,500 characters |
Clipped text ends with … [N more chars].
Scheduler
From apps/cli/src/runner/scheduler.ts.
| Limit | Value |
|---|---|
| Duplicate window | A routine that ran within 50 seconds (or half its interval, if shorter) is skipped |
| Definition refresh | Every 5 minutes, and 1 second after any routine.* or agent.* event |
Stale-run expiry (--expire-interval) | Every 60 seconds by default |
| Cron fields | 5, or 6 with seconds |
| Missed ticks | Not backfilled |
sat doctor
| Limit | Value |
|---|---|
/health timeout | 10 seconds |
Live stream wait (--stream-timeout) | 20 seconds by default |