Use the Skillset REST API to read and create tickets and search knowledge articles from your own scripts and services.
https://app.skillset.co/api/v1
All requests and responses use JSON. Requests act as the Skillset user who owns the credential and are limited to that user's access.
Send a credential as a Bearer token on every request:
Authorization: Bearer
| API key | OAuth | |
|---|---|---|
| Best for | Scripts, cron jobs, back-end services | Apps where each user signs in |
| Setup | Create in Skillset settings | Automatic client registration |
| Lifetime | 30, 90, or 365 days, or no expiration | Short-lived access token plus refresh token |
| Works with MCP | No | Yes, with a token issued for the MCP resource |
Store keys in a secret manager. To rotate, create a new key, deploy it, then revoke the old one; revocation takes effect on the next request.
Warning: Never put a key in a URL, source control, logs, or a support message.
Skillset supports OAuth 2.1 with automatic client registration, so no client ID or secret needs to be issued to you.
| Item | Value |
|---|---|
| Resource | https://app.skillset.co/api/v1 |
| Protected resource metadata | https://app.skillset.co/.well-known/oauth-protected-resource/api/v1 |
| Authorization server metadata | https://clerk.skillset.co/.well-known/oauth-authorization-server |
| Client registration | Dynamic Client Registration (RFC 7591) or Client ID Metadata Documents |
| Grant type | Authorization code with PKCE (S256) |
| Refresh | Request the offline_access scope |
token_endpoint_auth_method=none) with a loopback redirect URI.response_type=code, your scopes, a random state, a PKCE challenge, and resource=https://app.skillset.co/api/v1.state on the callback and exchange the code and PKCE verifier at the token endpoint.Refresh with grant_type=refresh_token and the same resource. Each refresh token works once: store the new pair, and never run two refreshes at the same time, or the grant may be revoked. Revoke tokens at the revocation endpoint.
Note: Tokens are bound to one resource. A token for the MCP server (https://app.skillset.co/api/v1/mcp) is rejected by the REST API, and the reverse.
| Scope | Grants |
|---|---|
knowledge:read | Search and read knowledge articles |
tickets:read | List and read tickets |
tickets:write | Create and update tickets |
tickets:delete | Delete tickets |
Each request must have the required scope and be allowed by the user's current workspace role. Scopes never add workspace access, and write or delete scopes do not include read.
1. List your workspaces.
curl https://app.skillset.co/api/v1/me/contexts \
-H "Authorization: Bearer $SKILLSET_API_KEY"
[
{
"tenant_id": "11111111-1111-4111-8111-111111111111",
"tenant_slug": "example",
"tenant_name": "Example",
"role": "member",
"workspaces": [{ "slug": "support", "name": "Support", "role": "member" }]
}
]
2. Use a tenant_id and workspace slug in the path.
WS=https://app.skillset.co/api/v1/tenants/$TENANT_ID/workspaces/support
curl "$WS/tickets?limit=10" -H "Authorization: Bearer $SKILLSET_API_KEY"
curl "$WS/knowledge/search?q=password%20reset" -H "Authorization: Bearer $SKILLSET_API_KEY"
The same calls in Python (pip install httpx):
import os
import httpx
ws = f"/api/v1/tenants/{os.environ['TENANT_ID']}/workspaces/support"
with httpx.Client(
base_url="https://app.skillset.co",
headers={"Authorization": f"Bearer {os.environ['SKILLSET_API_KEY']}"},
) as client:
tickets = client.get(f"{ws}/tickets", params={"limit": 10})
tickets.raise_for_status()
print(tickets.json())
Workspace endpoints are relative to /api/v1/tenants/{tenant_id}/workspaces/{workspace_slug}.
| Method | Path | Scope | Description |
|---|---|---|---|
GET | /api/v1/me/contexts | none | Organizations and workspaces you can access |
GET | /tickets | tickets:read | List tickets |
GET | /tickets/{id} | tickets:read | Get a ticket |
POST | /tickets | tickets:write | Create a ticket |
PATCH | /tickets/{id} | tickets:write | Update a ticket |
DELETE | /tickets/{id} | tickets:delete | Delete a ticket |
GET | /knowledge/articles | knowledge:read | List articles |
GET | /knowledge/search | knowledge:read | Search articles |
GET | /knowledge/articles/{article_id} | knowledge:read | Get an article |
{id} accepts the ticket UUID or its short ID, such as TKT-42.
List query parameters
| Parameter | Type | Description |
|---|---|---|
status | string | Filter by a status configured in the workspace |
priority | string | Filter by a priority configured in the workspace |
q | string | Text search on title and description |
limit | integer | 1–200, default 50 |
offset | integer | Default 0 |
Create body
| Field | Type | Required | Description |
|---|---|---|---|
title | string | Yes | Up to 500 characters |
description | string | No | Up to 50,000 characters |
priority | string | No | A priority configured in the workspace |
Update accepts any of title, description, status, and priority. Requester, creator, and source come from the credential; other fields are rejected with 422.
curl -X POST "$WS/tickets" \
-H "Authorization: Bearer $SKILLSET_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: 7f3c2a10-provision-laptop" \
-d '{"title": "Provision laptop for new hire", "priority": "high"}'
{
"id": "0b6f6f6e-2c1d-4c39-9d43-3d5f3a7e9b21",
"short_id": "TKT-131",
"title": "Provision laptop for new hire",
"description": null,
"status": "new",
"priority": "high",
"created_at": "2026-10-19T04:10:00Z",
"updated_at": "2026-10-19T04:10:00Z",
"resolved_at": null
}
Ticket creation requires the workspace to enable Set-up → Channels → API. Delete moves the ticket to the workspace Trash, where it can be restored for 30 days. Tickets linked to an external system such as Zendesk or Jira cannot be deleted through the API.
| Endpoint | Parameters | Returns |
|---|---|---|
GET /knowledge/articles | limit 1–200 (default 50), offset | articles, total, limit, offset |
GET /knowledge/search | q (required), limit 1–25 (default 10), offset | results with article_id, title, snippet, score |
GET /knowledge/articles/{article_id} | none | Full article with content |
Results include only collections the user can read. Knowledge is read-only.
Send an Idempotency-Key header on POST /tickets to make retries safe. A repeat with the same key and credential returns the original ticket with 200 instead of creating a duplicate (201). Use a new key for each new ticket. Keys are scoped to the credential, so check for the ticket before retrying with a rotated key.
List endpoints use limit and offset and return total. Increase offset by limit until it reaches total.
| Bucket | Limit |
|---|---|
Reads (GET) | 120 requests per minute |
| Writes (all other methods) | 30 requests per minute |
Limits apply per user, shared across all of that user's API keys, OAuth clients, and MCP sessions. Over the limit, the API returns 429 with a Retry-After header in seconds.
{
"error": {
"code": "insufficient_scope",
"message": "Scope required: tickets:read",
"request_id": "8cbb07d77357492b",
"details": { "scope": "tickets:read" }
}
}
Match on code; message is human-readable and may change.
| Status | Code | Meaning |
|---|---|---|
| 401 | unauthorized | Missing, expired, or revoked credential |
| 403 | insufficient_scope | Credential lacks the scope in details.scope |
| 403 | forbidden | User is not a member of the organization or workspace |
| 403 | api_intake_disabled | Workspace does not accept API tickets |
| 403 | collection_not_readable | User cannot read the article's collection |
| 404 | not_found | Workspace, ticket, or article not found |
| 409 | provider_linked_ticket_delete_unsupported | Ticket is linked to an external system |
| 422 | invalid_request | Invalid input; see details.problems |
| 429 | rate_limited | Wait for Retry-After |
| 503 | verification_unavailable | Credential could not be verified; retry |
Include the request_id when contacting Skillset support.