CrewLink API

Admin (CrewLink staff)

The admin area is for CrewLink's own staff, not for customers. Super admins see every company, open any of them as its owner for an hour to help (audited, with limits), look at platforms and their grants, manage who else is a super admin, and read the audit log. A company never needs any of this; it's documented so the people who run CrewLink, and the developers after them, know exactly what it can and can't do.

#Who is a super admin

CrewLink runs an internal company, slug crewlink, kind: "platform". It is never billed, has no booking page (GET /v1/public/crewlink is 404), and can't be a CrewConnect partner (404 to connect to it). Its people sign in like anyone else: an emailed code or a passkey (sign-in.md). A person there whose superAdmin flag is on is a super admin; GET /v1/office/me says "superAdmin": true.

Everything under /v1/admin needs a super admin signed in as themselves:

Token /v1/admin/*
A super admin's session or user token Yes
Anyone else at crewlink (staff who aren't super admins) 403 forbidden
Any other company's people, API key or a platform key 403 forbidden (401 for a bad token)
An act-as session, even a super admin's 403 acting_as (exit first)

The flag only counts on the crewlink company: set on anyone else's user it does nothing.

#GET /v1/admin/companies

Every company, newest activity first.

Query
q Part of the name or slug, any case (% and _ are literal)
kind company, individual, client or platform
limit Default 100, at most 500
bash
curl -s "$CREWLINK/v1/admin/companies?q=north" -H "Authorization: Bearer $ADMIN"
json
{
  "total": 1,
  "companies": [
    {
      "id": "3f6c2a9e-8b1d-4c7a-9e2f-5d4b1a7c8e90",
      "slug": "northline",
      "name": "Northline Heating & Air",
      "kind": "company",
      "plan": "crew",
      "planStatus": "trialing",
      "demo": true,
      "managedBy": null,
      "counts": { "users": 3, "techs": 8, "jobs": 2412, "jobs30d": 611 },
      "createdAt": "2026-10-10T14:02:11.000Z",
      "lastActivityAt": "2026-10-13T15:41:08.000Z"
    }
  ]
}
Field Notes
plan, planStatus The CrewLink plan and its Stripe status; null until the company has a subscription row
demo A demo account (filled by cli.ts demo-fill)
managedBy Client companies: the provider that set them up
counts Active sign-ins, active people on the schedule, jobs all time and created in the last 30 days
lastActivityAt The latest job created or sign-in started; act-as sessions don't count

Counts only: no customers, addresses or job details. To see those, act as the company.

#POST /v1/admin/companies/{id}/act-as

Opens a 1-hour session as the company's owner, to see and fix what they see (support, a sales demo).

bash
curl -s -X POST "$CREWLINK/v1/admin/companies/$COMPANY/act-as" -H "Authorization: Bearer $ADMIN" \
  -H 'content-type: application/json' -d '{"reason": "ticket 1042: board looks empty"}'
json
{ "token": "9b3e...-....-....-............_secret", "expiresAt": "2026-10-13T16:41:08.000Z", "company": { "id": "3f6c...", "name": "Northline Heating & Air", "slug": "northline" } }

Use token as a bearer token on the office API. GET /v1/office/me answers as the owner with userId: null and actingAs: { adminUserId, adminName, expiresAt }. End it early with POST /v1/auth/logout using the act-as token.

What it can do: everything the company's owner can do in the office API, with these exceptions, which answer 403 acting_as:

Never while acting
/v1/admin/* Exit first: an act-as session can't act as another company or promote anyone
POST /v1/office/payouts..., POST /v1/payments/... Payouts and payments
PATCH /v1/office/plan, writes under /v1/office/billing The plan, billing and the card on file
POST /v1/office/pay/periods/{start}/close, /paid Closing or paying a pay week
/v1/office/api-key/..., /v1/office/integration-key/..., /v1/office/clients/{id}/api-key, POST /v1/office/users/{id}/token Any key or sign-in token
Writes under /v1/office/platforms Platform grants
/v1/auth/passkeys... Passkeys (they belong to a person)

Nothing filed while acting is credited to a person (it acts like the company API key: createdByUserId is null), and Idempotency-Keys are kept per admin and company.

Audit. Opening it, every request made with it (method, path, status) and ending it each write a row to the audit log with the admin's user id and the company. The session also stops working the moment the admin is no longer an active super admin.

Errors
404 not_found No company with that id
422 invalid_request That's the crewlink company itself

#GET /v1/admin/platforms

Every platform (platforms.md) with whether its key is disabled, its calls in the last 7 days and every grant, live and revoked, newest first:

json
{
  "platforms": [
    {
      "id": "6d1f...", "slug": "acme", "name": "Acme", "createdAt": "2026-10-12T09:00:00.000Z", "disabledAt": null, "calls7d": 18234,
      "grants": [
        { "company": { "id": "3f6c...", "name": "Northline Heating & Air", "slug": "northline" }, "scopes": ["read", "webhooks", "write"], "grantedAt": "2026-10-12T09:01:00.000Z", "revokedAt": null, "grantedBy": null }
      ]
    }
  ]
}

grantedBy is the owner's user id, or null when CrewLink granted it (cli.ts platform-grant). Platforms are still created and granted on the server (cli.ts platform-create, platform-grant); this is read-only.

#GET /v1/admin/users and POST /v1/admin/users/{id}/superadmin

GET /v1/admin/users lists CrewLink's own people: { id, name, email, role, active, superAdmin, inviteSentAt }.

To add a super admin: sign in to CrewLink, add the person under Users (POST /v1/office/users, which emails them an invite), then:

bash
curl -s -X POST "$CREWLINK/v1/admin/users/$USER/superadmin" -H "Authorization: Bearer $ADMIN" \
  -H 'content-type: application/json' -d '{"superAdmin": true}'

{"superAdmin": false} takes it away; their act-as sessions end at once.

Errors
404 not_found Not one of CrewLink's own people (a customer's user can never be a super admin)
409 last_super_admin Demoting the last active super admin. Promote someone else first
422 inactive Promoting someone whose sign-in is off

Both are audited (superadmin_grant, superadmin_revoke).

#GET /v1/admin/actions

The audit log, newest first. Filter with companyId or adminUserId; page back with before (an id); limit default 100, at most 500.

json
{
  "actions": [
    { "id": 812, "admin": { "id": "a1...", "name": "Sam Admin", "email": "sam.admin@example.com" }, "company": { "id": "3f6c...", "name": "Northline Heating & Air", "slug": "northline" }, "action": "act_as_request", "method": "POST", "path": "/v1/office/jobs/4411/move", "status": 200, "sessionId": "9b3e...", "detail": {}, "at": "2026-10-13T15:52:40.000Z" },
    { "id": 811, "admin": { "id": "a1...", "name": "Sam Admin", "email": "sam.admin@example.com" }, "company": { "id": "3f6c...", "name": "Northline Heating & Air", "slug": "northline" }, "action": "act_as_start", "method": "POST", "path": "/v1/admin/companies/3f6c.../act-as", "status": 200, "sessionId": "9b3e...", "detail": { "reason": "ticket 1042" }, "at": "2026-10-13T15:41:08.000Z" }
  ]
}
action
act_as_start An act-as session opened (detail.reason if one was given)
act_as_request One request made with it, written once the response has gone out
act_as_end Ended with POST /v1/auth/logout (a session that simply expires has no end row)
superadmin_grant, superadmin_revoke Someone was made, or stopped being, a super admin (detail.userId)

Rows are never deleted.

#The ops pages

GET /v1/ops/travel and GET /v1/ops/brain, owner only for a company, are also open to super admins: they're platform-wide numbers (road router, drive-time cache, traffic lookups, rush-hour profiles, brain training runs). The web app's Admin page shows them as tiles.

CrewLink API · Reference · Changelog