VetoVetoDocs
Hosted REST API

Auth & sign-in

Magic-link and CLI device-code sign-in. These public endpoints mint the API key everything else needs.

The /v1/auth/* routes are public — they take no API key because they are how you get one. There are two flows that share one magic link: a browser sign-in and a CLI device-code poll. Completing either mints a veto_… key (broad default scopes) bound to your project.

Flow at a glance

StartPOST /v1/auth/email/start with your email (and, for the CLI, a device_code). A magic link is emailed.
Verify — the user clicks the link (GET /v1/auth/verify?token=…), which provisions the account and mints the key.
Poll (CLI only) — POST /v1/auth/cli/poll returns pending until the link is clicked, then ready with the api_key.

POST /v1/auth/email/start

Trigger a magic link. Always returns 200 for a valid email (no account enumeration).

AuthPublic

Request

{
  "email": "you@example.com",
  "device_code": "optional — present for the CLI device flow"
}
FieldRequiredNotes
emailValidated; invalid → 400 EMAIL_INVALID.
device_codeoptionalBinds this link to a CLI poll. Bad format → 400 DEVICE_CODE_INVALID.

Response — 200

{ "ok": true, "expires_in": 900 }

Dev convenience

Outside production (NODE_ENV !== 'production') the response also includes verify_url and dev_token so the flow is automatable without SMTP. In production the link is emailed only and never returned.

Status codes

StatusReason codeMeaning
200Link issued (or silently no-op for enumeration safety).
400EMAIL_INVALID / DEVICE_CODE_INVALIDBad input.
429RATE_LIMITEDToo many sign-in requests; back off ~15 min.
start sign-in
curl -X POST https://api.veto-ai.com/v1/auth/email/start \
  -H "Content-Type: application/json" \
  -d '{"email":"you@example.com"}'

GET /v1/auth/verify

The link the browser opens. Provisions the account on first use, mints the key, and renders a small HTML confirmation page (not JSON). If a device_code was attached, it also flips the CLI poll to ready.

AuthPublic (single-use token in the query string)
StatusMeaning
200Verified — HTML "You're signed in" page.
400Missing / invalid / already-used token.
410The link expired — sign in again.
curl "https://api.veto-ai.com/v1/auth/verify?token=<token>"

POST /v1/auth/cli/poll

Long-poll for the CLI device flow. Returns pending until the user clicks the link, then ready once — with the minted key.

AuthPublic

Request

{ "device_code": "the code shown by the CLI" }

Responses

200 — pending
{ "status": "pending" }
200 — ready
{
  "status": "ready",
  "api_key": "veto_test_8f2c…",
  "project_id": "proj_01J…",
  "org_id": "org_01J…"
}

Status codes

StatusReason codeMeaning
200pending or ready (see above).
400DEVICE_CODE_INVALIDMissing / malformed device_code.
404DEVICE_CODE_NOT_FOUNDUnknown device code.
410MAGIC_LINK_EXPIREDThe sign-in link expired before it was clicked.
poll until ready
curl -X POST https://api.veto-ai.com/v1/auth/cli/poll \
  -H "Content-Type: application/json" \
  -d '{"device_code":"WDJB-MJHT"}'

Where to next

Once you hold a key, see Authentication for headers, scopes, and mode isolation — then create a merchant.