s46.apiv1

Admin

Organization, people, team, runtime, usage, and audit routes.

Manage organizations, people, teams, workers, jobs, schedules, usage, and audit. Everything under /v1/admin requires an owner or admin role for the target org/team.

Capability groups

GroupPrefixManages
Organizations/v1/admin/orgsOrg settings, slugs, and region/residency defaults.
People/v1/admin/peopleDirectory, invitations, role assignment, and removal.
Teams/v1/admin/teamsTeam CRUD, membership, default model, and mode.
Workers/v1/admin/workersRegistered worker fleet and host visibility.
Jobs/v1/admin/jobsControl-plane job queue: inspect, retry, cancel.
Schedules/v1/admin/schedulesRegion scheduling policy (the field is region, never lane).
Usage/v1/admin/usagePer-team spend and request counters.
Audit/v1/admin/auditAppend-only audit log of privileged actions.

Shared conventions

  • List routes are cursor-paginated (limit + opaque cursor).
  • Mutations accept an Idempotency-Key; reusing it with different parameters returns 409 idempotency_conflict.
  • Updates to mutable resources use If-Match; a stale revision returns 412 precondition_failed.
  • All path identifiers (team, email, ids) must be URL path-escaped.

People & invitations

GET/v1/admin/people?team={team}200
POST/v1/admin/people/invite200

List directory members or send an invitation. Invites gate the device-login flow — an email with no active invitation fails login with 403 not_invited.

Auth · owner / admin · Schema · illustrative

# List people
curl "https://api.s46.dev/v1/admin/people?team=%40s46%2Fengineering" \
  -H "Authorization: Bearer $S46_ACCESS_TOKEN"

# Invite someone
curl -X POST https://api.s46.dev/v1/admin/people/invite \
  -H "Authorization: Bearer $S46_ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: 9b7c1e0a-2f4d-4c8e-bf1a-1e2d3c4b5a6f" \
  -d '{ "email": "newhire@s46.dev", "team": "@s46/engineering", "role": "member" }'
await fetch("https://api.s46.dev/v1/admin/people/invite", {
  method: "POST",
  headers: {
    Authorization: `Bearer ${accessToken}`,
    "Content-Type": "application/json",
    "Idempotency-Key": crypto.randomUUID(),
  },
  body: JSON.stringify({ email: "newhire@s46.dev", team: "@s46/engineering", role: "member" }),
});
import uuid

requests.post(
    "https://api.s46.dev/v1/admin/people/invite",
    headers={"Authorization": f"Bearer {access_token}",
             "Idempotency-Key": str(uuid.uuid4())},
    json={"email": "newhire@s46.dev", "team": "@s46/engineering", "role": "member"},
)
{
  "email": "newhire@s46.dev",
  "team": "@s46/engineering",
  "role": "member"
}

Usage & audit

GET/v1/admin/usage?team={team}200
GET/v1/admin/audit?team={team}200

Usage reports per-team spend and request counters; audit is an append-only log of privileged actions. Both are cursor-paginated.

Auth · owner / admin · Schema · illustrative

curl "https://api.s46.dev/v1/admin/audit?team=%40s46%2Fengineering&limit=50" \
  -H "Authorization: Bearer $S46_ACCESS_TOKEN"
const audit = await fetch(
  "https://api.s46.dev/v1/admin/audit?team=%40s46%2Fengineering&limit=50",
  { headers: { Authorization: `Bearer ${accessToken}` } },
).then((r) => r.json());
audit = requests.get("https://api.s46.dev/v1/admin/audit",
                     params={"team": "@s46/engineering", "limit": 50},
                     headers={"Authorization": f"Bearer {access_token}"}).json()
{
  "data": [
    {
      "at": "2030-01-01T12:00:00Z",
      "actor": "dscape@s46.dev",
      "action": "people.invite",
      "target": "newhire@s46.dev",
      "team": "@s46/engineering"
    }
  ],
  "hasMore": false,
  "nextCursor": null
}

On this page