Skip to main content
Alongside the /v1 developer API, Square1 exposes a second HTTP surface that manages the account itself — API keys, browser sessions, your own usage metrics, password and passkeys, and BYOK credentials. This is the surface the Square1 dashboard talks to. It is documented here because a handful of things (listing your keys, revoking a lost session, pulling your own per-model metrics) have no /v1 equivalent.
Read this before you build against it. These endpoints are not a product API. They are not versioned, they predate the /v1 surface’s conventions, and the shapes below reflect what the dashboard happens to need. Treat every response as additive-only and pin nothing.

Base URL

All paths on this page are relative to that origin. The /v1 endpoints do not live here, and these endpoints do not live on inference.square1.dev — see Base URLs.

Authentication

Two credentials are accepted, and which ones work depends on the endpoint. Signing in mints an HttpOnly, Secure, SameSite=Lax cookie named ws-session, scoped to / with a 30-day lifetime. Every endpoint on this page accepts it. The sign-in routes that mint it (POST /api/auth/login, POST /api/auth/passkey/verify, and the community-verification signup flow) are outside the scope of this page.

Bearer API key — dual auth

Most /api/user/* endpoints also accept an ordinary API key:
This is the same key the /v1 surface uses. When you authenticate this way there is no session row behind your request — which matters for Sessions, where current is then false on every row. BYOK management is the exception: /api/user/me/byok* accepts a Bearer key too, but applies a stricter gate. See BYOK management.
Cookie auth is not practical for unattended automation. ws-session is minted by a login form that sits behind a Cloudflare Turnstile challenge when the operator has it enabled — there is no headless credential exchange. If you are scripting, use a Bearer API key on the dual-auth endpoints; reuse an existing browser session only for the handful of endpoints that require the cookie.

Account state

An account that is unlinked or revoked can still reach most of this surface — that is deliberate, because self-recovery happens in the dashboard. Two things are locked down anyway:
  • Minting credentials (POST /api/user/me/keys, POST /api/user/me/rotate-key) requires active, and answers 403 account_unlinked otherwise.
  • BYOK requires active as well.
suspended and retired accounts are rejected outright.
The two credentials are not exactly equivalent: a revoked account can still reach this surface with the cookie but not with a Bearer key. active and unlinked work either way.
The full state table lives in Authentication.

Response envelopes

Most endpoints answer with:
That is the majority, not the rule. The exceptions on this surface: Read for the field you want; don’t assume a wrapper.

Field naming

Key casing is not consistent, including inside a single response. This is a known wart, not a pattern you can predict.
  • /api/user/* request bodies are mostly camelCase (currentPassword, profileUrl) — but POST /api/user/sfw takes sfw_mode.
  • /api/user/me/byok* is entirely snake_case, request and response.
  • GET /api/user/me mixes both in one payload: profile, modelUsage, and totals are camelCase while display_id, today_self, recentRequests[] rows, and everything under plan are snake_case.
  • GET /api/user/me/metrics is entirely snake_case.
Copy the exact spelling from the tables on each page.

Errors

Errors mostly use the same envelope as the /v1 surface:
Two differences from /v1 errors:
  1. code is frequently absent. Many failures on this surface carry only message and type. Where a code exists it is listed explicitly on each page — branch on it there, and fall back to the HTTP status everywhere else.
  2. One endpoint returns a bare string. GET /api/user/me answers 403 with {"error": "User not found (legacy key)"}error is a string, not an object. If you unwrap error.message blindly you will get undefined here.
As always, message is Korean and is written for humans. Don’t parse it.

Rate limits

Only some paths carry an application-level window. Each is 60 seconds, fixed (not sliding), and counted per client IP — not per account. Everything else under /api/user/* — including the key list, key creation/deletion, session management, passkeys, and GET /api/user/me — has no application-level window. Edge limits still apply.

Rate-limit headers

Requests that pass through one of the windows above carry exactly two headers, on both accepted and rejected requests:
There is no X-RateLimit-Reset, and the 429 from these windows carries no Retry-After. The window is a fixed 60 seconds; wait that long. Paths without a window emit neither header.
A rejection from these windows is:
Separately, POST /api/user/me/rotate-key enforces its own 60-second cooldown instead of a window, and its 429 carries no code and no Retry-After. Handle a bare 429 as retryable.

Browser access

Cross-origin browser access is not supported. These endpoints are served for the Square1 web app’s own origin, and the custom response headers above are not exposed to cross-origin JavaScript. If you need this data in a page you host, proxy it through your backend with a Bearer key.

Pages

API keys

List, create, delete, reveal, and rotate your sq-arca- keys.

Sessions

See where you’re signed in and cut off a lost device.

Profile and usage

GET /api/user/me, your own metrics, and your recent requests.

BYOK management

Register and verify your own provider credentials.

Password and passkeys

Change your password; manage WebAuthn credentials.