Skip to content

Hub API Reference ​

The Sevorix Hub is the account and artifact backend behind hub.sevorix.com. Its REST API is what an external integrator — or the sevorix/sevsh CLI's own hub client — calls to register an account, manage signing keys, and publish or search artifacts.

Base URL ​

https://hub.sevorix.com

Every path in this reference is relative to that origin, and every API path is prefixed /api/v1. A self-hosted or preview instance serves the same routes under its own hostname — substitute it for the base URL and nothing else in these examples changes.

Conventions ​

  • Requests and responses are JSON. Any endpoint that takes a body expects Content-Type: application/json; a body the server cannot deserialize is rejected with 400.
  • Authentication is a bearer token, never a cookie. See Authentication.
  • Timestamps are RFC 3339 / ISO 8601 in UTC, e.g. 2026-03-14T09:21:07.481293Z.
  • Identifiers (id, user_id, artifact_id) are UUIDs, rendered as lowercase hyphenated strings.
  • Absent values are null, not omitted — an optional field is present in the response with a null value.

Error responses ​

Every error the API itself raises has the same body shape:

json
{ "error": "artifact 'net-egress@1.0.0' already exists" }

The message is written to be shown to a person. A client should prefer it over anything it could reconstruct from the status code alone.

Two failures do not use that shape, and a client that assumes JSON will mis-handle both:

  • 429 Too Many Requests comes from the rate limiter in front of the handler. Its body is plain text with no Content-Type header at all.
  • Malformed path parameters (a :user_id that is not a UUID, say) are rejected by the framework's own extractor before the handler runs, with a plain-text 400.

Parse defensively: attempt JSON, fall back to the raw body, fall back to the status.

Status codes ​

The hub is deliberate about which status it returns, and the distinctions carry information a caller can act on.

StatusMeaning here
200 OKSuccess, with a body.
201 CreatedA new resource was created (register, push, key, endorsement).
202 AcceptedThe request was accepted; the effect is asynchronous. Only password-reset requests return this.
204 No ContentSuccess, with no body (deletes, revocations, yanks).
400 Bad RequestThe request itself is wrong: a malformed body, a field that fails validation, a value out of range.
401 UnauthorizedYou have not established who you are, or a credential you supplied did not check out. See 401 vs 403.
403 ForbiddenYou are known, and not permitted to do this. Retrying with the same account will not help.
404 Not FoundThe resource does not exist — or it exists and is not yours to see. See 404 as a privacy boundary.
409 ConflictA uniqueness constraint was violated: an email already registered, an artifact version already published, a key already known, an artifact already endorsed by you.
429 Too Many RequestsA rate limit was hit. Plain-text body — see above.
500 Internal Server ErrorAn unhandled failure. The body is always the generic "internal server error"; details are logged server-side only.
503 Service UnavailableA dependency this endpoint needs is not configured on this instance. Only password-reset requests return this.

401 and 403 are not interchangeable in this API, and neither is 404 where artifacts are concerned. All three are covered in detail on the Authentication page.

Rate limiting ​

Limits are per client IP address, enforced as token buckets in front of the handlers. A request that exceeds one never reaches the handler.

ScopeLimit
POST /api/v1/login10 requests/minute
PATCH /api/v1/me/password10 requests/minute
POST /api/v1/password-reset/confirm10 requests/minute
POST /api/v1/register5-request burst, refilling at 1/minute
POST /api/v1/password-reset/request5-request burst, refilling slowly
Everything else1000-request burst, refilling at ~17/second

The stricter buckets guard endpoints that run an Argon2 hash on caller-supplied input, or that cost an outbound email. Password resets are additionally capped per account at 3 tokens per hour, independently of which IP asks — see Password reset.

Browser callers (CORS) ​

The hub is called directly from a browser frontend on a different origin, so it sends CORS headers — but from a compiled-in allow-list of origins, not a wildcard. A page served from an origin that is not on that list will have its requests blocked by the browser regardless of whether the request itself would have succeeded.

Access-Control-Allow-Credentials is deliberately not sent. Authentication here is a bearer header and never a cookie, so there is no ambient credential for a cross-origin request to carry.

Liveness ​

GET /health

Unauthenticated. Returns 200 with an empty body as long as the process is up and serving. It deliberately does not touch the database, so it stays meaningful when the database or billing dependencies are degraded — it answers "is this process alive", not "is this service healthy end to end".

In this section ​

  • Authentication — obtaining a token, presenting it, password rules, the reset flow, and what each kind of rejection means.
  • Endpoints — method, path, request and response shapes, and error responses for every public route.

Runtime containment for autonomous AI agents.