Skip to content

docs: explain shorebird api, pagination and error documentation_url - #726

Merged
eseidel merged 9 commits into
mainfrom
eseidel/shorebird-api-docs
Oct 16, 2026
Merged

eseidel merged 9 commits into
mainfrom
eseidel/shorebird-api-docs

Conversation

@eseidel

@eseidel eseidel commented Oct 16, 2026 •

Copy link
Copy Markdown
Contributor

The CLI now has shorebird api, an authenticated passthrough to the Shorebird API meant mainly for coding agents and scripts, but the API reference page (which every API error's documentation_url now links to) only described raw HTTP with a bearer token. This adds a "Calling the API from the command line" section to account/api.mdx, and updates the page's common patterns for the error body's documentation_url and the new cursor pagination.

What changed

src/content/docs/account/api.mdx:

  • New section on shorebird api: what it is for (mainly agents and scripts; only the body on stdout; non-zero exit on failure; --jq applies only to successful responses), discovery via the OpenAPI spec, one example (listing an organization's members), and a pointer to shorebird api --help for all options. The CLI documents its own flags, so the page doesn't repeat them.
  • Errors: lists documentation_url and says to branch on code.
  • Pagination: per_page (default 30, max 100), cursor and next_cursor on the apps, releases and patches lists, with a loop that walks every page and stops when next_cursor is absent. Notes that some newer list endpoints page by default.

src/content/docs/code-push/ci/scripting.mdx: notes that shorebird api rejects --json.

Agent entry points (agent-instructions.txt, the llms.txt details in astro.config.mjs, agent-skills/index.json): mention shorebird api.

Release timing

shorebird api shipped in Shorebird 1.6.129, and the page says it needs 1.6.129 or newer. Ready to land.

Testing

  • Every example checked against packages/shorebird_cli/lib/src/commands/api_command.dart on shorebird main.
  • Read-only prod checks with a CLI built from shorebird main: /apps (80 apps), /apps?per_page=2 (2 apps plus next_cursor), following next_cursor with -X GET -f per_page=2 -f cursor=... (next 2 apps), and cursor=garbage (400 invalid_cursor with documentation_url, exit 1).
  • npm run build (astro check and build): passes, 68 pages.
  • Prettier, cspell and lint-component-labels: clean. Vale was not run (not installed locally).

@eseidel

eseidel commented Oct 16, 2026

Copy link
Copy Markdown
Contributor Author

Adversarial review (separate agent):

Tier 2, one docs file. Every claim checked against api_command.dart, jq.dart and retrying_client.dart on shorebird main, and against the server's pagination.dart, the paged list routes and openapi.yaml. Head ecf10442.

Sev Scope Where Issue Action
🔴 Blocker RelatedFsNotInDiff src/content/docs/account/api.mdx:155 → jq.dart _index The pagination example fails for anyone whose list fits on one page, which is most accounts at per_page=50. On the last page the server leaves next_cursor out, --jq '.next_cursor' prints null, and line 156 sends cursor=null, which the server rejects with 400 invalid_cursor and exit 1. An empty value fails the same way. Guard the second call, for example --jq '.next_cursor | select(. != null)' plus [ -n "$cursor" ] && shorebird api ..., or show a short while loop that stops when the cursor is empty.
🟡 Watch RelatedFsNotInDiff src/content/docs/account/api.mdx:38 → shorebird packages/shorebird_cli/pubspec.yaml shorebird api is not in any tagged release: main is still at version 1.6.128, and the command (#4082) is not in v1.6.128. Docs publish on merge, so landing now documents a command nobody can run yet. The wording "releases after 1.6.128" also differs from the docs' usual phrasing: other pages say "needs Shorebird X.Y.Z or newer". Hold the merge until the next CLI release, then change the line to "shorebird api needs Shorebird 1.6.129 or newer." with the actual version number.
🟡 Watch RelatedFsNotInDiff src/content/docs/account/api.mdx:73 → api_command.dart _run --jq only applies to 2xx responses. On an error the raw JSON error body still goes to stdout, so x=$(shorebird api ... --jq '.id') stores the error body in x. Scripts that check only the output and not the exit status will treat the error as data. Add to the --jq bullet: "On a non-2xx response the query is not applied and the error body is printed instead, so check the exit status."

Checked and accurate: the default method and when it switches to POST; -f/-F typing, @path/@-, and {appId} in -F values only; --input moving fields to the query string; the API-host-only URL check; the list of supported jq features; exit status 1 with a one-line stderr error; GET-only retries (5xx and connection errors); SHOREBIRD_TOKEN; the ErrorResponse fields including documentation_url; per_page default 30 and maximum 100; apps, releases and patches lists being opt-in, while invitations, audit logs and previews page by default; and the four example endpoints and the granularity=day value.

@eseidel

eseidel commented Oct 16, 2026

Copy link
Copy Markdown
Contributor Author

Addressed in 1de9ef9:

  1. The pagination example is now a loop that stops when a page has no next_cursor (--jq '.next_cursor | select(. != null)' prints nothing then; the CLI's jq subset has select but not //), and exits on any failed request. Verified read-only against prod with a CLI built from shorebird main: per_page=50 walks 80 apps in two pages and stops; a last page yields an empty cursor.
  2. Version wording: left the page as "available in Shorebird CLI releases after 1.6.128". TODO at release time: change it to the house style ("needs Shorebird X.Y.Z or newer") with the first release that contains shorebird api.
  3. Added: --jq applies only to successful responses; on an error the raw error body goes to stdout and the exit status is 1, so scripts should check the exit status. Verified: shorebird api '/apps?cursor=garbage' --jq '.apps' prints the error body and exits 1.

@eseidel
eseidel enabled auto-merge (squash) October 16, 2026 02:04

@AbhishekDoshi26 AbhishekDoshi26 left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed at ff9526e.

Verdict: Changes requested. The facts on the page check out against source, but the agent entry points and one other page are out of step with it, and there are two small nits.

Checked

  • shorebird api behavior in shorebird packages/shorebird_cli/lib/src/commands/api_command.dart (origin/main):
    • Only the response body goes to stdout (runWithStdoutOnStderr).
    • A non-2xx response prints the raw body and exits 1 (requestFailedExitCode).
    • --jq is applied only when succeeded.
    • Requests are authenticated with the login session or SHOREBIRD_TOKEN.
    • -X GET with -f puts the fields in the query string.
  • The command is not in v1.6.128 (pubspec.yaml on main is still 1.6.128). shorebird metrics is also not in v1.6.128 (added in shorebird#4083). The "releases after 1.6.128" marker covers both.
  • shorebird account orgs prints the org id first (account/orgs_command.dart). shorebird apps list and shorebird releases list exist.
  • The jq subset in jq.dart supports select and !=. select(. != null) on a missing next_cursor prints nothing, so the loop's [ -n "$cursor" ] || break ends it correctly.
  • Example GET /organizations/{organizationId}/users:
    • The route exists (routes/api/v1/organizations/[organizationId]/users/index.dart) and is not paged.
    • The response shape users[].user.email matches GetOrganizationUsersResponse, OrganizationUser and PublicUser in public/openapi.yaml.
  • Pagination in lib/src/pagination.dart:
    • per_page defaults to 30 and is clamped to 100.
    • next_cursor is left out on the last page.
    • A bad cursor returns 400 invalid_cursor.
  • Paged routes:
    • Apps, releases and patches list everything unless per_page or cursor is sent (PageRequest.read without pagedByDefault).
    • Invitations, audit logs and previews page by default.
    • /apps returns apps[].app_id (GetAppsResponse, AppMetadata).
  • ErrorResponse has code and message (required) plus details and documentation_url (openapi.yaml). The error_documentation_url.dart middleware adds documentation_url.
  • The anchor #common-patterns resolves to the ## Common patterns heading.
  • No em-dashes in added lines. Prettier, cspell and vale are clean on src/content/docs/account/api.mdx.
  • Existing review items (cursor loop, version wording, --jq on errors) are addressed and not raised again.

Findings

  1. Agent entry points not updated.

    • Where: public/.well-known/agent-instructions.txt:31, astro.config.mjs:228, public/.well-known/agent-skills/index.json:43.
    • Problem: The page now documents a new CLI command that is aimed at agents and scripts. None of the three hand-maintained agent entry points mention it. They still describe the API only as raw HTTP with a bearer token.
    • Fix, agent-instructions.txt item 2: append "From a shell, shorebird api <endpoint> (Shorebird CLI releases after 1.6.128) calls any endpoint with your shorebird login session or SHOREBIRD_TOKEN, prints only the response body, and exits 1 on a non-2xx response; --jq filters successful responses. Guide: https://docs.shorebird.dev/account/api.md"
    • Fix, astro.config.mjs "Code Push REST API" line in the details: append " From the CLI, 'shorebird api ' (releases after 1.6.128) sends authenticated requests without handling tokens (reference: https://docs.shorebird.dev/account/api.md)."
    • Fix, index.json: change the shorebird-api description to "Programmatic access to Shorebird apps, releases, artifacts, and channels via REST OpenAPI 3.1, or from the CLI with shorebird api."
  2. shorebird api rejects --json, but another page says other commands accept it.

    • Where: src/content/docs/code-push/ci/scripting.mdx:63-65, together with src/content/docs/account/api.mdx:35.
    • Problem: scripting.mdx says commands not in its table "accept --json and run non-interactively". shorebird api is not in the table, and it does not accept --json: _run calls usageException('shorebird api prints the response body as-is and does not support --json.') when isJsonMode. An agent following scripting.md that adds the global --json gets a usage error. The new section does not mention this either.
    • Fix, api.mdx line 35: change "Only the response body goes to stdout," to "Only the response body goes to stdout (the global --json flag is not supported),".
    • Fix, scripting.mdx after line 65: add "shorebird api is the exception: it prints the API response body as-is and rejects --json. See Calling the API from the command line."
  3. Mixed punctuation in the Common patterns list.

    • Where: src/content/docs/account/api.mdx:133.
    • Problem: The PR changed the **Errors** bullet from an em-dash to a colon and added **Pagination**: with a colon. The third bullet in the same list still follows **Unauthenticated endpoint** with an em-dash, so the list mixes styles and keeps an em-dash on a page this PR edits.
    • Fix: - **Unauthenticated endpoint**: POST /patches/check is the one exception to
  4. The pagination loop closes an interactive terminal on failure.

    • Where: src/content/docs/account/api.mdx:127-128.
    • Problem: The loop uses || exit 1. The intro calls it "This loop", which invites pasting it into a terminal. If a request fails there (for example, when not logged in), exit 1 closes the user's terminal.
    • Fix: change the intro on line 119 to "This script prints every app ID, one page of 50 at a time, stops when a page has no next_cursor, and exits with status 1 if a request fails:". Keep the code as is.

@AbhishekDoshi26 AbhishekDoshi26 left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed at b4244e0.

Verdict: two findings left. The four findings from the previous review are fixed correctly, and every factual claim on the page matches source.

Checked

  • Previous fixes: the agent entry points (agent-instructions.txt, the llms details in astro.config.mjs, the shorebird-api entry in index.json) now mention shorebird api with wording that matches source. api.mdx and scripting.mdx say --json is rejected, matching the usageException in api_command.dart. The **Unauthenticated endpoint** bullet uses a colon, and the loop intro says "This script".
  • api_command.dart: the response goes through runWithStdoutOnStderr, requestFailedExitCode is 1, --jq applies only on success, and -X GET with -f sends query parameters.
  • jq.dart: a missing key yields null, and select drops it, so .next_cursor | select(. != null) prints nothing on the last page and the loop ends. .apps[].app_id matches AppMetadata in public/openapi.yaml.
  • _shorebird pagination.dart: per_page defaults to 30 and is clamped to 100, and next_cursor is left out on the last page. Apps, releases and patches return everything without per_page or cursor. Invitations, audit logs, previews and preview notes page by default.
  • GET /organizations/{organizationId}/users is readable by every role down to viewer (permission.dart), and users[].user.email matches the response.
  • Anchors resolve, there are no em-dashes, and Prettier, vale and cspell are clean on the changed text.

Findings

  1. src/content/docs/account/api.mdx:30-31: shorebird metrics is not in a released CLI. The section lists it among the everyday commands, but it was added in shorebird#4083 after v1.6.128, and the page's version note covers only shorebird api. Fix: "For everyday tasks, use the dedicated commands (shorebird apps list, shorebird releases list, shorebird patches list, and so on)."
  2. The PR description no longer matches the diff. It says "two minimal examples" (there is one), Testing mentions {appId} examples that no longer exist, and "What changed" omits the scripting.mdx note and the agent entry point updates. Fix: update those three places.

@AbhishekDoshi26 AbhishekDoshi26 left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed at df27327.

Verdict: clean, no findings at any severity. Both earlier rounds' findings are fixed, the description matches the diff, and every factual claim matches source.

Checked

  • Earlier findings: shorebird metrics is gone from the everyday-commands list. The description now covers one example, the scripting.mdx note and the agent entry points. The round 1 fixes are still in place.
  • api_command.dart (shorebird main): only the body goes to stdout (runWithStdoutOnStderr), --json raises a usageException, a non-2xx response prints the raw body and exits 1, --jq runs only on success, credentials come from the shorebird login session or SHOREBIRD_TOKEN, and -X GET with -f sends query parameters.
  • shorebird account orgs, apps list, releases list and patches list all exist in v1.6.128.
  • jq.dart: select and != are supported, so the pagination loop ends on the last page.
  • pagination.dart (_shorebird): per_page defaults to 30 and is clamped to 100, next_cursor is left out on the last page, and a bad cursor returns 400 invalid_cursor. Apps, releases and patches return everything unless a page is requested. Invitations, audit logs, previews and preview notes page by default.
  • The example endpoint /organizations/{organizationId}/users is a GET gated on .view(.organizationMembers), and users[].user.email matches the response types.
  • error_documentation_url.dart adds documentation_url to every JSON 4xx/5xx body with a code.
  • The agent entry points are accurate, index.json parses, the anchors resolve, there are no em-dashes, and Prettier, cspell and vale are clean on the changed text.

Findings

None.

@eseidel
eseidel merged commit 8497ecd into main Oct 16, 2026
4 checks passed
@eseidel
eseidel deleted the eseidel/shorebird-api-docs branch October 16, 2026 16:37
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants