Alaskan Labs
Docs · API reference

API

API reference

The Alaskan for Business API (v1). Signing in with email, linking a browser or an iPhone, workspaces and dogs, Decide and undo, the Ledger and its CSV export, templates and workflows.

The web app and the iPhone app use one API. Every call is a POST with a JSON body unless it says otherwise. Errors are {error} with a 4xx status.

Who's asking

Every /v1/workspace* and /v1/decisions* call (and /v1/link/list, /revoke, /join, /v1/auth/me and /signout) is made as a person:

  • the iPhone app sends X-Alaskan-User,
  • a browser sends Authorization: Bearer <web token>, from an email sign-in or from a phone that linked it.

Neither, or a revoked, signed-out or unknown token, is a 401. A person without a workspace gets 404 from everything but /v1/workspace and /v1/workspace/create. Browsers may call these paths from the web app's own origin (CORS); no cookies are involved.

Signing in on the web

  • POST /v1/auth/email/start {email} (no auth) → {ok: true, email, expiresAt}. Emails a sign-in link and a 6-digit code, valid for 15 minutes, once. At most 5 an hour to one address and 20 an hour from one network (429); 400 for something that isn't an email.
  • POST /v1/auth/email/verify {token} or {email, code} → {token, email, isNew}: a web token. The first sign-in for an address makes its account (isNew: true); later ones are the same person, on any browser. 410 for a used or expired link or code, 401 for a wrong code; five wrong codes end that sign-in (429).
  • POST /v1/auth/me → {kind: "email", email} for an account made with an email, {kind: "phone"} for a browser signed in as a phone.
  • POST /v1/auth/signout → {ok: true}; that token is a 401 from then on.

Links, codes and web tokens are stored only as hashes.

Linking a browser to a phone

  • POST /v1/link/start {label?} (no auth) → {code, display, expiresAt, pollToken}. code is 8 characters with no 0, O, 1 or I; display is the same as ABCD-2345. Valid for 10 minutes, once. label names the browser in the phone's list (by default from its User-Agent, "Chrome on Mac").
  • POST /v1/link/confirm {code, name?} (the phone only) → {ok: true, label}. Any case, with or without the dash. 404 for a wrong code, 409 used, 410 expired, 403 from a browser.
  • POST /v1/link/poll {pollToken} → {status: "waiting" | "linked" | "expired", token?, name?}. Poll every couple of seconds. The token comes once; a poll after that says expired.
  • POST /v1/link/list → {browsers: [{id, label, createdAt, lastSeenAt}]}.
  • POST /v1/link/revoke {id} → {ok: true}; that browser's token is a 401 from then on.

Poll tokens and web tokens are stored only as SHA-256 hashes.

Bringing an iPhone into a workspace

  • POST /v1/link/join (someone in the workspace, usually from the web) → {code, display, expiresAt, pollToken}: the same kind of code, valid 10 minutes, once. The web shows it with a QR code.
  • The phone types it where it types any browser code and sends the usual POST /v1/link/confirm {code, name?}. For a join code, the phone's person joins the workspace as a member → {ok: true, label, joined: {workspace}}. 409 if the phone already belongs to another workspace (one per person in v1).
  • POST /v1/link/join/poll {pollToken} → {status: "waiting" | "joined" | "expired", member?: {id, name, role}}.
  • On the phone, the join shows in /v1/link/list as " on the web"; revoking it takes the phone out of the workspace (an owner is never removed).

Workspace

  • POST /v1/workspace → {workspace: Workspace | null}.
  • POST /v1/workspace/create {name, template?, ownerName?, timeZone?} → {workspace}, from a team template (default starter) with its dogs, its workflows installed and sample work. timeZone is IANA (default UTC). 409 if the person already has a workspace; one per person in v1.
  • POST /v1/workspace/dog {id, rules?, name?, routines?: [{id, enabled}]} → {dog}. rules replaces all three lists.
  • POST /v1/workspace/routine {dogId, id, enabled} → {dog}.
  • POST /v1/workspace/hire {template, dogId} → {dog}: adds that team template's dog with its job, rules and routines. Up to 12 dogs; 409 when a dog of that name is on the team.
  • POST /v1/workspace/turn {dogId, text} → {messages: [Message]}: what was typed and the dog's reply (with decisionId when it proposed something).
  • POST /v1/workspace/messages {dogId} → {messages: [Message]}, oldest first, the last 100.
  • POST /v1/workspace/sessions {dogId?} → {sessions: [Session]}, newest first.
  • POST /v1/workspace/computer → {computer: Computer}; /computer/start, /computer/stop.

Decisions and the Ledger

  • POST /v1/decisions {status?: "pending" | "all", dogId?} → {decisions: [Decision], counts: {pending, today}}. pending (the default) is the queue, oldest first; all is newest first.
  • POST /v1/decisions/decide {id, option?, typed?} → {decision}. option is an option's key (1–4). The decision is declined when that option's effect is decline, otherwise approved. A typed answer is kept exactly as typed and counts as approved. undoUntil is 10 seconds on. 409 if it's already decided or expired.
  • POST /v1/decisions/undo {id} → {decision}, only before undoUntil. A person's answer goes back to pending; a rule's becomes undone.
  • POST /v1/decisions/outcome {id, status: "good" | "bad" | "unknown", note?} → {decision}, on a decided one.
  • POST /v1/decisions/events {id} → {events: [{id, at, actor: {kind: "person" | "rule" | "dog" | "system", name}, action, detail}]}: the decision's audit trail, oldest first. Append-only.
  • GET /v1/decisions/export.csv → the Ledger as CSV. See The Ledger.

Templates and workflows

  • GET /v1/templates (no auth) → {teams: [...], workflows: [...]}: every team template with its dogs, and every workflow template with its trigger, node count and apps.
  • GET /v1/templates/team/<slug>, GET /v1/templates/workflow/<slug> → the template itself.
  • POST /v1/workspace/workflows → {workflows: [{id, slug, name, summary, for, dog, dogId, trigger, enabled, installedAt, nodes, apps, lastRun}]}.
  • POST /v1/workspace/workflows/install {slug} → {workflow}. Installing twice keeps the first.
  • POST /v1/workspace/workflows/run {id} → {session}, as it stands when the run finishes or waits at a Decide.

Alaskan bots

Bots built by others: the public marketplace (GET /v1/marketplace/bots), adding, running and removing a bot in a workspace (/v1/workspace/bots/*), and the creator calls (/v1/creators/*). They're all in the Alaskan bot reference.

Shapes

Workspace { id, name, plan: "trial" | "business", sample: boolean, template, timeZone, createdAt,
            you: Member, members: [Member], dogs: [Dog], computer: Computer,
            dashboard: {views: [View, with each metrics item's value]}, counts: {pending, today} }
Member    { id, name, role: "owner" | "member", app: "web" | "iphone" }
Dog       { id, key, name, job, breed: "malamute" | "husky" | "kleekai",
            coat: "aurora" | "ember" | "glacier" | "twilight", lead: boolean, tagline, persona,
            status: {working, needsYou, queued}, preview, lastActivityAt, screen,
            rules: {justDo: [string], ask: [string], never: [string]},
            routines: [{id, title, when, nextAt, enabled}], connectors: [string],
            hoursThisWeek }
Session   { id, dogId, title, status: "running" | "needs_you" | "queued" | "done", screen,
            costCents, startedAt, endedAt?, steps: [{at, kind: "look" | "think" | "act" | "result",
            text}], sample, workflowId?, runId? }
Decision  { id, dogId, kind: "send" | "update" | "book" | "spend" | "publish" | "other",
            title, summary, source, items: [{label, field, from?, to, flagged?}],
            options: [{key: 1–4, label, detail, effect: "approve" | "decline", value?}], reasoning,
            evidence: [{label, url?}], thread: [{from, at, text}], status: "pending" | "approved" |
            "declined" | "auto" | "undone" | "expired", chosen?, typed?, decidedBy?: {kind:
            "person" | "rule", name}, decidedAt?, undoUntil?, expiresAt?, outcome?: {status:
            "pending" | "good" | "bad" | "unknown", note, checkAt?, checkedAt?}, createdAt, sample,
            workflowId?, runId? }
Computer  { provider, state: "demo" | "off" | "starting" | "on", screens: [{n, dogId, title,
            activity}], costPerHourCents, hoursThisWeek, note?, error? }
Message   { id, role: "user" | "dog", dogId, text, at, decisionId?, sample? }

value on an option is a workflow template's own key for it, when that isn't its number. A member's app is where they came in: an email account on the web, or the iPhone app. decidedBy.name is a person's name, or the rule with its kind (Just do: Refunds up to $50). screens[].activity is the dog's running session's latest step.

Limits

A workspace's people may send its dogs 400 messages a UTC day (fair use); past that, the dogs say so until midnight UTC.