API Overview
REST, JSON, API-key auth. Available on every plan, free included.
Authentication
A workspace in the app is a project in the API; its slug is the project parameter.
Generate a key under Settings → API Keys (Admin role required). You'll see the key value exactly once, copy it somewhere you trust before closing the dialog, because we can't show it again. Pass it on every request as a Bearer token:
curl -H "Authorization: Bearer ld_your_api_key_here" \
https://api.fesk.io/v1/tickets
The X-API-Key header works as well; a key in the URL (?api_key=) is refused with 400. Keys can be scoped to a workspace and can have an expiry date, after which they answer 401. Revoke a leaked key from the same settings page.
Grant only the scopes the integration needs, such as tickets:read, tickets:write, or comments:write. A write scope includes the read scope of the same resource (tickets:write reads tickets too, comments:write reads comments), and * grants everything. A project-scoped key is the simplest option. If a key is not tied to a project, ticket and board requests must identify one, either a numeric project_id or a human-readable project slug (e.g. ?project=infrastructure); otherwise the API returns 400.
Discovering projects
You don't need to know internal project IDs. Call GET /v1/projects with any valid key to list the projects it can reach, each with a stable slug you can use in place of the numeric ID:
curl -H "Authorization: Bearer ld_your_api_key_here" \
https://api.fesk.io/v1/projects
{
"ok": true,
"data": {
"projects": [
{ "id": 3, "name": "Infrastructure", "slug": "infrastructure", "description": null }
],
"count": 1
}
}
Anywhere an endpoint needs a project, you may pass ?project=<slug> instead of ?project_id=<id>. A project-scoped key resolves the project automatically and needs neither.
Base URL
https://api.fesk.io/v1/
External integrations use the api. subdomain with a /v1 version prefix, for example https://api.fesk.io/v1/tickets. API keys only work on this subdomain; sending one to the main domain returns 401.
Response shape
Every response uses the same envelope, so you can write one client that handles them all:
{
"ok": true,
"data": { }
}
On errors, ok is false and there's an error object instead of data:
{
"ok": false,
"error": {
"code": 422,
"message": "Validation failed",
"validation_errors": { }
}
}
The X-Correlation-ID response header contains a request identifier, a 32-character hex string such as 81132ce3df94f6e6d855bb563299f637. Quote it when reporting bugs, it lets us trace your exact request through our logs.
Status codes
400, malformed JSON, no project named, or the key in the URL401, missing, invalid or expired API key402, the included API allowance or configured monthly API limit has been reached403, authenticated but not permitted: the key lacks the scope, orproject_idnames a workspace the key cannot reach404, resource doesn't exist, or is in another workspace than the request's422, validation failed;error.validation_errorshas field-level messages429, rate-limited; check theRetry-Afterheader5xx, something on our side; theX-Correlation-IDheader is what to send us
Safe retries
For every POST, for PUT /v1/tickets/{id} and for PATCH /v1/tickets/{id}/status, send a stable Idempotency-Key header (1 to 128 characters) when an operation may be retried. Repeating the same method and path with the same payload and key returns the stored successful result instead of applying the operation twice. Other updates ignore the header. Use a new key whenever the intended payload changes or the user explicitly reapplies an operation.
Rate limiting
Requests are rate-limited per API key using a sliding window. When you hit the limit, the API returns 429 with a Retry-After header. Wait at least that long; repeated clients should use exponential backoff with jitter.
API calls are also metered for billing. The first 5,000 calls per month are included. An admin can enable paid API usage or set a lower monthly call limit under Settings → Plan & Usage; reaching either boundary returns 402 with the reason and current usage.
Every response to an API-key request says what is left of the month: X-Allowance-Remaining counts the calls left before 402, X-Allowance-Reset is when the count starts again, and X-Allowance-Warning: low appears once a tenth or less is left. A client that does a lot of work in one go should read it and stop at a clean point. The account's admins are notified in the app when the allowance runs low.
Common endpoints
| Method | Path | What it does |
|---|---|---|
GET | /v1/projects | List accessible projects and their slugs |
GET | /v1/tickets | List tickets, paginated and filterable |
POST | /v1/tickets | Create a ticket |
GET | /v1/tickets/:id | Get one ticket with full detail |
PUT | /v1/tickets/:id | Update fields on a ticket |
That's a fraction of what's there, for the full list with request and response schemas, see the interactive API reference.