Skip to content
Desk · API reference

Helpdesk API docs: REST endpoints and webhooks.

Drive Flaxxa Desk from your own software: a REST API with workspace keys, signed webhooks and idempotency keys.

No credit card
Free plan
Cancel anytime
flaxxa.com / product / desk / docs
cURLREST · JSON
# Flaxxa Desk · first request
curl https://flaxxa.com/api/v1/desk \
  -H "Authorization: Bearer $FLAXXA_KEY" \
  -H "Accept: application/json"

# webhooks: X-Flaxxa-Signature (HMAC-SHA256)
# retries: Idempotency-Key, 24 h
200 OKapplication/json
+ webhooks · HMAC-signed
REST
Requests per key by default
120/min
Idempotency window
24h
Quick start

How do I make my first API call?

Replace $FLAXXA_KEY with a key from your workspace. The first call returns a real id you can use for the next ones.

cURL
curl -X POST https://flaxxa.com/api/v1/desk/tickets \
  -H "Authorization: Bearer flx_live_YOUR_KEY" \
  -H "Accept: application/json" -H "Content-Type: application/json" \
  -d '{
    "department_id": 3,
    "subject": "Order #18429 not delivered",
    "description": "Order was due yesterday — still no update.",
    "priority": "high",
    "type": "incident",
    "source_channel": "api",
    "contact": { "email": "[email protected]", "name": "Jane Doe" }
  }'
PHP
$ticket = Http::withToken(env('FLAXXA_DESK_KEY'))->acceptJson()
    ->post('https://flaxxa.com/api/v1/desk/tickets', [
        'department_id'  => 3,
        'subject'        => "Order #{$order->id} not delivered",
        'description'    => $customerMessage,
        'priority'       => 'high',
        'source_channel' => 'api',
        'contact'        => ['email' => $customer->email, 'name' => $customer->name],
    ])->throw()->json();

// $ticket['id'], $ticket['ticket_number'] (e.g. "DESK-1042"), $ticket['status']
Node
const res = await fetch("https://flaxxa.com/api/v1/desk/tickets", {
  method: "POST",
  headers: { Authorization: `Bearer ${process.env.FLAXXA_DESK_KEY}`, Accept: "application/json", "Content-Type": "application/json" },
  body: JSON.stringify({
    department_id: 3,
    subject: "Order #18429 not delivered",
    description: "Order was due yesterday — still no update.",
    priority: "high",
    contact: { email: "[email protected]", name: "Jane Doe" },
  }),
});
const ticket = await res.json(); // ticket.ticket_number
Python
import requests
r = requests.post("https://flaxxa.com/api/v1/desk/tickets",
    headers={"Authorization": f"Bearer {KEY}", "Accept": "application/json"},
    json={"department_id": 3, "subject": "Order #18429 not delivered",
          "description": "Order was due yesterday — still no update.", "priority": "high",
          "contact": {"email": "[email protected]", "name": "Jane Doe"}})
r.raise_for_status()
print(r.json()["ticket_number"])
Request
POST /api/v1/desk/tickets HTTP/1.1
Host: flaxxa.com
Authorization: Bearer flx_live_·············
Content-Type: application/json
Idempotency-Key: order-18429-ticket

{
  "department_id":  3,
  "subject":        "Order #18429 not delivered",
  "description":    "Order was due yesterday — still no update.",
  "priority":       "high",
  "type":           "incident",
  "source_channel": "api",
  "contact":        { "email": "[email protected]", "name": "Jane Doe" }
}
Response · 201
HTTP/1.1 201 Created
Content-Type: application/json

{
  "id": 9127,
  "ticket_number": "DESK-1042",
  "status": "open",
  "priority": "high",
  "type": "incident",
  "subject": "Order #18429 not delivered",
  "department": { "id": 3, "name": "Support" },
  "contact":    { "id": 551, "name": "Jane Doe", "email": "[email protected]" },
  "assigned_agent": null,
  "created_at": "2026-10-27T10:00:00+00:00"
}
Authentication

How do I authenticate?

Every call requires Authorization: Bearer <key>. Rotate from workspace settings — old keys are revoked instantly. Server-side only; never ship a live key to the browser.

Base URLhttps://flaxxa.com/api/v1/desk
Tickets

Tickets API

Open, read, update and move tickets. The customer can be given inline as `contact: { email, name }` — found by email or created — or as a `contact_id`.

POST/tickets

Create ticket

Body: department_id (required), subject, description, contact | contact_id, priority (urgent|high|medium|low), type (question|incident|problem|task|feature_request|bug), category_id, source_channel, cc_addresses[], bcc_addresses[]. Emails the customer the ticket. Scope desk.ticket.create.

GET/tickets/by-number/{ticket_number}

Retrieve by number

The ticket customers quote (e.g. DESK-1042) with department, contact, agent and messages. Scope desk.ticket.view.

GET/tickets/{id}

Retrieve ticket

Full ticket: messages (public + private), attachments, SLA binding, watchers, todos, notes, tags.

GET/tickets

List tickets

Paginated; filter with status, priority, department_id, assigned_agent_id, q (search); page, per_page.

PUT/tickets/{id}

Update ticket

subject, priority, type, department_id, product_id, category_id, assigned_team_id, due_at, meta_data. Scope desk.ticket.edit.

POST/tickets/{id}/status

Change status

{ status: new|open|pending|on_hold|waiting_customer|resolved|closed|cancelled, reason? }. Fires desk.ticket.status_changed.

POST/tickets/{id}/assign

Assign

{ agent_id }. Scope desk.ticket.assign. Fires desk.ticket.assigned.

POST/tickets/{id}/merge

Merge

Merge another ticket into this one. Scope desk.ticket.merge.

POST/tickets/bulk

Bulk action

Status / assignment / delete across many tickets. Scope desk.ticket.edit.

DELETE/tickets/{id}

Delete (trash)

Soft-delete; POST /tickets/{id}/restore brings it back. Scope desk.ticket.delete.

Replies & notes

Replies & notes API

A reply goes to the customer by email (or the ticket's channel); a note stays internal.

POST/tickets/{id}/reply

Reply

{ body (HTML or text), is_private?, channel?, to_addresses[], cc_addresses[], bcc_addresses[], attachments[] }. Scope desk.reply.send. Fires desk.ticket.replied.

POST/tickets/{id}/note

Internal note

{ body }. Visible to agents only. Scope desk.note.create.

GET/tickets/{id}/discussion

Agent discussion

The agent-side thread on a ticket; POST …/discussion/messages to add to it.

GET/tickets/{id}/time-entries

Time entries

GET lists, POST { minutes, note } adds. Scope desk.ticket.view.

Contacts

Contacts API

The people who raise tickets, found or created by email.

POST/contacts/upsert

Find or create by email

{ email (required), name, phone, mobile, meta_data }. 201 when created, 200 when found (fields merged). Scope desk.ticket.create.

GET/contacts

List contacts

Search and paginate; each row carries its ticket counts.

GET/contacts/{id}

Retrieve contact

Contact with ticket history.

POST/contacts/{id}/block

Block sender

Quarantine future inbound from this email; /unblock reverses. Scope desk.contact.manage (admin).

Departments, categories, custom fields

Departments, categories, custom fields API

The ids you pass when creating tickets.

GET/departments

List departments

id + name of every department (department_id for ticket create).

POST/departments

Create department

{ name, description, parent_department_id, is_active }. Scope desk.department.manage.

GET/categories

List categories

Ticket categories (category_id).

POST/categories

Create category

{ name, description, color, position }. Scope desk.settings.manage.

GET/custom-fields

List custom fields

Field definitions per entity_type (ticket|contact|company). Scope desk.settings.manage.

PUT/custom-fields/values/{entity_type}/{entity_id}

Set custom values

{ values: { field_key: value } } on a ticket or contact. Scope desk.ticket.view.

GET/sla-policies

SLA policies

GET lists; POST { name, priority_targets.{urgent|high|medium|low}.{first_response|resolution} } creates. Scope desk.settings.manage.

GET/canned-responses

Canned responses

GET lists; POST { title, content, category, scope: personal|department|global, department_id }. Scope desk.reply.send.

Knowledge base

Knowledge base API

Public help-centre articles and the SOP library the AI copilot answers from.

GET/kb/categories

KB categories

GET lists; POST { name, description, icon, position }. Scope desk.sop.manage.

GET/kb/articles

KB articles

GET lists; POST { category_id, name, body, excerpt, is_published }; PUT/DELETE /kb/articles/{id}.

GET/sop-libraries

SOP libraries

Scope desk.sop.view. POST /sops { library_id, title, body } adds a procedure; /sops/{id}/activate publishes it.

Webhooks

Webhooks API

POST /api/v1/webhooks { url, subscribed_events[] } — scope workspace.webhooks.manage. Deliveries carry X-Flaxxa-Event and X-Flaxxa-Signature (HMAC-SHA256 of the raw body with the endpoint secret, shown once).

POST/../webhooks

Subscribe

Register a URL + the desk.* events you want.

GET/../webhooks

List

Endpoints with failure counts and last delivery.

DELETE/../webhooks/{id}

Unsubscribe

Stops delivery.

Webhook events

Which webhook events can I subscribe to?

Subscribe via POST /webhooks. Verify X-Flaxxa-Signature on every payload.

  • desk.ticket.created

    A ticket was opened, from any channel or the API.

  • desk.ticket.resolved

    A ticket was marked resolved.

  • desk.ticket.closed

    A ticket was closed.

  • desk.ticket.replied

    A reply was posted on a ticket (agent or customer).

  • desk.ticket.status_changed

    A ticket moved between statuses (open → pending → resolved → closed …).

  • desk.ticket.assigned

    A ticket was assigned or reassigned to an agent.

  • desk.ticket.priority_changed

    A ticket's priority changed.

  • desk.ticket.escalated

    A ticket was escalated, by an agent or an automation.

  • desk.csat.submitted

    The customer rated a resolved ticket.

Errors

What errors can the API return?

All errors return a JSON envelope: { error: { code, message, details } }. Retry idempotent calls on 5xx with exponential backoff.

CodeNameWhen it fires
401unauthenticatedAPI key missing, expired or revoked.
403insufficient_scopeKey valid but lacks the scope — desk.ticket.create to open, desk.reply.send to reply, desk.ticket.view to read.
404not_foundNo ticket / contact with that id or number in this workspace.
422validation_failed{ message, errors } — e.g. department_id unknown, contact or contact_id missing, status not in the allowed set.
429rate_limited120 requests/minute per key by default; Retry-After is set.
Conventions

Which rules apply to every request?

Every Flaxxa app's API follows the same authentication, rate limits, idempotency and webhook signing.

Bearer auth

Every API call uses a Bearer token. Keys belong to a workspace and are scoped per app. Create and revoke them from workspace API settings.

Rate limits

Each key can make 120 requests a minute unless the app sets its own limit. Going over returns 429 with a Retry-After header.

Idempotency

Send an Idempotency-Key header on a POST and a retry with the same key within 24 hours returns the first response instead of running again.

Signed webhooks

Every webhook payload carries an X-Flaxxa-Signature HMAC-SHA256 header made with the secret you get when you subscribe. Verify it before processing.

API FAQ

Flaxxa Desk API: common questions

Frequently asked questions

Yes. The REST API at https://flaxxa.com/api/v1/desk uses workspace API keys with scopes. You can create, read, update, assign and merge tickets, reply, add notes and manage contacts, departments and knowledge base articles.

Get started in minutes

Ship faster with Flaxxa Desk.

Create a workspace, make an API key in its settings, and send your first request.

Free plan · No credit card · Cancel anytime