Skip to content

API overview ​

The Standcast API lets your systems read your organization's data and receive events. It is versioned under /v1 and described by an OpenAPI 3.1 document.

  • Base URL: https://api.standcast.app
  • OpenAPI document: https://api.standcast.app/openapi.json
  • Interactive reference, with every operation, parameter, example and error: API reference

What the API offers ​

  • Read organization data with an API key:

    OperationScopeReturns
    GET /v1/org/teamsteams:readThe teams in standcast.yml: name, time zone, workdays, times and members
    GET /v1/org/dailiesdailies:readConfirmed dailies, filtered by date and team
    GET /v1/org/dailies/{id}dailies:readOne daily
    GET /v1/org/digestsdigests:readDigest deliveries, with who did not send the daily, filtered by team, from and to
    GET /v1/org/blockersblockers:readConfirmed blockers, filtered by date and team
    GET /healthnoneService health
  • Receive events with webhooks: daily.submitted, blocker.opened and digest.sent.

The API returns only what people confirmed and what already appears in the digest. It never returns drafts, suggestions, per-person metrics, rankings or aggregates.

What the API does not offer ​

You cannot send a daily through the public API. AI agents send dailies through the MCP server or the Claude Code skill, and scripts through the CLI.

Why: Standcast never sends a daily on someone's behalf without their explicit confirmation. That rule lives in one place, the MCP server and the skill, which show the person the daily and send it only after they confirm. An API key belongs to the organization, not to a person, so it cannot carry that confirmation. The routes the CLI uses to send dailies are internal, authenticated with a person's session, and not part of the public contract.

Quick start ​

bash
curl -H "Authorization: Bearer $STANDCAST_API_KEY" \
  https://api.standcast.app/v1/org/teams
json
{
  "data": [
    {
      "name": "Alpha",
      "timezone": "America/Sao_Paulo",
      "workdays": ["mon", "tue", "wed", "thu", "fri"],
      "remindAt": "09:00",
      "digestAt": "10:00",
      "memberLogins": ["alice", "bob"]
    }
  ],
  "next_cursor": null
}

Authentication ​

The public Standcast API authenticates with an API key. Send it as a bearer token on every request:

http
GET /v1/{resource} HTTP/1.1
Host: your-standcast-host
Authorization: Bearer sck_xxxxxxxxxxxxxxxxxxxxxxxx

API keys are prefixed with sck_. Every operation under /v1/org requires one. Only sck_ keys are accepted there: a session token (sc_) sent to /v1/org fails with 401 unauthorized, and an API key sent to a session-only route fails the same way.

Creating a key ​

Only an organization admin can create keys. An admin signs in with GitHub, opens the Standcast setup page of the organization and uses the API keys section: name the key, pick its scopes and create it. For a personal account, the account owner is the admin. Each key belongs to one organization and sees only that organization's data.

The full secret is shown once, when the key is created. Standcast stores only a hash and cannot show it again, so copy it right away. The list shows each key's prefix (the first 8 characters after sck_), scopes, creation date and last use, which is updated at most once per minute.

Scopes ​

Grant the narrowest scope that works.

ScopeAllows
teams:readGET /v1/org/teams
dailies:readGET /v1/org/dailies and GET /v1/org/dailies/{id}
digests:readGET /v1/org/digests
blockers:readGET /v1/org/blockers

A request that needs a scope the key does not have fails with 403 insufficient_scope. The error names the missing scope in required_scope:

json
{
  "error": {
    "code": "insufficient_scope",
    "message": "api key lacks the dailies:read scope",
    "required_scope": "dailies:read"
  }
}

Missing, malformed, unknown and revoked keys fail with 401 unauthorized. Each key is limited to 600 requests per minute (see Rate limits).

Revoking a key ​

An admin can revoke a key at any time. Revocation takes effect immediately and later requests with that key fail with 401 unauthorized. Rotate a key by creating the new one first, deploying it, then revoking the old one.

Storing keys safely ​

  • Keep keys in a secret manager or in environment variables, never in source control or client-side code.
  • Use one key per integration so a leak can be revoked without breaking the others.
  • Never log the Authorization header.
  • If a key may have leaked, revoke it immediately.

Session tokens (sc_) ​

Tokens starting with sc_ are session tokens issued to the Standcast CLI, skill and MCP server after a GitHub login. They are not an integration method and the operations they unlock are not part of this reference.

Errors ​

Every error response uses the same envelope:

json
{ "error": { "code": "invalid_daily", "message": "invalid daily", "issues": [] } }

code is the stable contract: branch on it, not on message. message is always English and may change. issues is present only on validation errors and lists the failing fields. required_scope is present only on scope errors.

CodeHTTP statusWhat to do
unauthorized401The credential is missing, invalid, expired or revoked. Authenticate again or use a valid key.
invalid_signature401Only on the GitHub webhook receiver: the X-Hub-Signature-256 header is missing or does not match the body. Nothing was processed.
invalid_request422The request is malformed. Check issues and fix the listed fields. A signed GitHub webhook body that is not JSON also returns this code.
invalid_daily422The daily payload failed validation. Check issues against the schema.
org_not_found404The organization does not exist or the Standcast app is not installed on it. Check the name and the installation.
org_not_configured409The organization has no valid standcast.yml. Add or fix the file in the .github repository, then retry.
not_in_team422The person is not listed in any team of this organization. Add them to a team in standcast.yml.
forbidden403The credential is valid but the person or organization is not allowed to perform this action.
insufficient_scope403The API key lacks the scope this operation needs. The error carries required_scope. Create a key with that scope.
not_found404The resource does not exist in the key's organization.
suggestion_already_decided409The suggested blocker was already decided the other way. Fetch the draft again; it no longer lists it.
invalid_state400An authorization link expired or was tampered with. Start the flow again.
installation_pending409The installation is still being processed. Retry in a few seconds.
access_denied403The person denied the authorization. Nothing was changed.
slack_oauth_failed502Slack rejected the connection. Try connecting again.
linear_oauth_failed502Linear rejected the connection. Try connecting again.
payload_too_large413Only on the GitHub webhook receiver: the body is larger than 1 MB. Nothing was processed.
rate_limited429Too many requests. Wait Retry-After seconds, then retry (see Rate limits).
internal500Unexpected server error. Retry with backoff and contact support if it persists.

Pagination ​

List operations use opaque cursors.

ParameterWhereDescription
limitqueryPage size, from 1 to 100. Defaults to 50.
cursorqueryThe next_cursor of the previous page. Omit it to get the first page.

A paginated response carries the items and a next_cursor:

json
{ "data": [], "next_cursor": "eyJpZCI6MTIzfQ" }

When next_cursor is null there are no more pages. Treat the cursor as an opaque string: do not parse it, build it or store it for long. Keep the same filters while paginating.

An invalid cursor or a limit outside 1 to 100 fails with 422 invalid_request. Dailies and digests are ordered newest first. For blockers, limit counts blockers, not dailies.

Rate limits ​

Limits use a fixed 60-second window.

CredentialLimit
Session token (sc_)120 requests per minute
API key (sck_)600 requests per minute
Unauthenticated requestsCounted per IP address
Starting a CLI session10 requests per minute per IP address

Limits apply per credential, so one noisy integration does not use up another's budget.

Headers ​

Rate-limited routes return these headers on every response:

HeaderMeaning
RateLimit-LimitRequests allowed in the window.
RateLimit-RemainingRequests left in the current window.
RateLimit-ResetSeconds until the window resets.

When you exceed the limit ​

The response is 429 with code rate_limited and a Retry-After header holding the seconds to wait:

http
HTTP/1.1 429 Too Many Requests
Retry-After: 23
RateLimit-Limit: 600
RateLimit-Remaining: 0
RateLimit-Reset: 23

Wait Retry-After seconds before retrying, and spread retries with jitter.

Versioning ​

The API is versioned in the path. /v1 is the current version. The info.version of the OpenAPI document is the Standcast release (for example 0.2.0).

  • Public beta. While Standcast is in 0.x, /v1 can still change in incompatible ways. Such changes are listed in the changelog before they ship, with as much notice as possible. The 90-day notice below applies from 1.0.
  • New fields may be added to responses at any time. Ignore fields you do not recognize.
  • New optional request parameters and new operations may be added to /v1 without notice.
  • A change that breaks existing clients ships only in /v2.
  • Before /v1 behavior is removed or changed incompatibly, the change is announced in the changelog at least 90 days in advance.

Every release is listed in docs/api/CHANGELOG.md, under the same version as the GitHub release.

See the changelog for every release.