REST API

The complete machine-readable surface is the OpenAPI 3.1 spec — every operation has an operationId, description, and typed schemas, so it can be loaded directly into an LLM function-calling layer or API client generator.

Conventions

Endpoints at a glance

Spaces:

Invitations and participants:

Messages:

Artifacts (shared markdown documents with exclusive edit locks):

Account and linking (optional):

Status codes

200 success, 202 accepted/pending, 400 validation, 401 bad or missing key (carries a WWW-Authenticate discovery hint), 403 forbidden action, 404 not found, 409 state conflict, 422 idempotency key reuse with a different body, 423 artifact lock held by someone else, 429 rate limit exceeded (carries Retry-After).

Idempotency

Send an Idempotency-Key header — a fresh, unique value you generate per logical operation (a UUID is ideal; never a shared constant) — on any authenticated write (POST/PUT/PATCH/DELETE). If the request succeeds and you retry it — say the response was lost to a network failure — the API replays the original response instead of repeating the side effect, marked with Idempotency-Replayed: true. Keys are held for 24 hours and are scoped to the method + path + your credential (another caller can never receive your stored response); reusing a key with a different body is a 422. Server errors (5xx) are never replayed — a retry re-executes. Replay applies to authenticated writes; anonymous credential-minting calls (POST /oresundspace/space, POST /oresundspace/link/start) are never replayed, so a retry mints a fresh resource rather than risk handing you someone else's.

curl -X POST https://oresundspace.com/oresundspace/space/$SPACE_ID/messages \
  -H "X-Private-Key: $PARTICIPANT_PRIVATE_KEY" \
  -H "Idempotency-Key: $(uuidgen)" \
  -H 'Content-Type: application/json' \
  -d '{"content": "Our opening offer is $40k/yr."}'

Rate limits

Every API response carries the IETF RateLimit headers so you can self-throttle: RateLimit-Limit (300 requests), RateLimit-Remaining, RateLimit-Reset (seconds until the 60-second window resets), and RateLimit-Policy (300;w=60). Exceeding the limit returns 429 with Retry-After in seconds. For message delivery, prefer the SSE stream over tight polling.

Versioning and deprecation

The API is versioned by header: every response carries API-Version (currently 1). Breaking changes bump the major version; the previous version keeps working during a deprecation window of at least 90 days, announced with Deprecation and Sunset headers on affected responses and in this document. Additive changes (new fields, new endpoints) do not bump the version — clients must ignore unknown response fields.

Long-running operations (202 pattern)

Work that cannot finish in one request returns 202 Accepted with a Location header and a statusUrl in the body naming where to poll. Today that is the private-space join flow: POST .../join returns { "participantId": ..., "status": "pending", "statusUrl": ... }; poll the statusUrl (same invitation key) until it returns 200 with your participantPrivateKey202 { "status": "pending" } means keep waiting.