Documentation
The API is for the link owner. It is not a public service and there is no public registration. Keys are scoped and revocable.
Authentication
Send an API key as a bearer token. Keys are stored only as a salted hash, so the value is shown once at creation and cannot be recovered afterwards.
curl -H "Authorization: Bearer $GO_API_KEY" \ https://go.arjav.is-cool.dev/api/v1/links
Scopes are explicit and are never implied: a key granted links:read cannot create, modify or delete anything. Owner allowlist membership is re-checked on every request, so removing an owner from configuration invalidates their keys immediately.
Endpoints
| Method | Path | Scope | Notes |
|---|---|---|---|
| GET | /api/v1/links | links:read | List links owned by the authenticated owner. |
| POST | /api/v1/links | links:write | Create a link. Optional custom slug; reserved slugs are rejected. |
| GET | /api/v1/links/{id} | links:read | Fetch one link. |
| PATCH | /api/v1/links/{id} | links:write | Update destination, title, activation, expiry or tags. |
| DELETE | /api/v1/links/{id} | links:write | Delete a link and its dependent rows. |
| GET | /api/v1/analytics | analytics:read | Aggregate click counts over a date range. |
| GET | /api/v1/experiments/{id} | analytics:read | Versioned config, results, a deterministic replay and a routing check. |
| GET | /api/health | — | Liveness. Constant response. Safe to expose. |
| GET | /api/ready | — | Readiness. Reports database reachability and configuration booleans. |
Rate limits
Limits are counted in Postgres, so they hold across every deployed instance. A limited response carries 429, a Retry-After header and the remaining budget. If the counter store is unreachable, nonessential paths fail open rather than break.
Errors
Errors use a fixed JSON envelope and never include driver messages, stack traces or row contents.
{ "error": "Slug is already taken." }- 400Malformed request body, query or path parameter.
- 401Missing, malformed or revoked API key.
- 403Valid key without the required scope, or not an allowed owner.
- 404No such resource, or one owned by a different owner.
- 409Slug already taken.
- 422Well-formed but invalid, for example an unusable destination URL.
- 429Rate limited. See the Retry-After header.
- 500Unexpected server error. Detail is never included in the response.
Short link behaviour
- A live link returns
302withCache-Control: no-store, so the redirect is never cached and a changed destination takes effect immediately. - Unknown, disabled and expired slugs all return an indistinguishable
404, so the set of live links cannot be probed. - Only
httpandhttpsdestinations are ever returned, validated again at redirect time. - Analytics are written after the redirect is sent. A failure to record a click never delays or fails the redirect.