Server API
Create contacts, conversations, tickets and feedback from your backend.
Base URL: https://desk.api.corplandtechnologies.com/v1 (locally http://localhost:5030/v1).
Authentication
Create a server key in Settings, Keys. Send it as a bearer token. Keys start with dsk_live_ or dsk_test_ and are shown once.
curl https://desk.api.corplandtechnologies.com/v1/workspace \
-H "Authorization: Bearer dsk_live_..."Never put a server key in front end code. The widget only needs the public pk_live_ key.
Every response is { "success": true, "data": ... }. Errors are { "message", "code" } with a 4xx or 5xx status. The limit is 600 requests a minute per key.
Workspace
GET /workspace
{ "name": "Kente Labs", "publicKey": "pk_live_...", "ventureId": "..." }Contacts
A contact you create through the API is always verified, because your server vouches for it.
POST /contacts
Creates the contact or updates the one with the same external_id.
{
"external_id": "user_123",
"name": "Ama Mensah",
"email": "ama@example.com",
"phone": "+233200000000",
"avatar": "https://example.com/ama.png",
"attributes": { "plan": "pro" }
}GET /contacts/:id
The contact with their conversations, tickets, feedback and recent events.
Conversations
GET /conversations
Query: status (open, pending, snoozed, resolved, active), contact_id, q, before (cursor from nextCursor), limit (up to 100).
POST /conversations
Starts a conversation as the contact, for example from your own contact form.
{
"contact": { "external_id": "user_123", "name": "Ama Mensah", "email": "ama@example.com" },
"body": "I cannot export my video",
"clientId": "form_8841"
}clientId makes the call safe to retry: the same id never creates a second message.
GET /conversations/:id, PATCH /conversations/:id
Update status, priority (low, normal, high, urgent), assigneeId with assigneeName, tags, subject, or snoozedUntil with status: "snoozed".
GET /conversations/:id/messages, POST /conversations/:id/messages
Replies as your team. Set author_name to show a name, and visibility: "internal" for a note the visitor never sees.
{ "body": "Fixed. Try again now.", "author_name": "Billing bot", "clientId": "bot_1" }Tickets
POST /tickets
{
"contact": { "external_id": "user_123" },
"type": "bug",
"title": "Export stuck at 99%",
"description": "Happens on every 4K export",
"priority": "high",
"tags": ["export"],
"context": { "pageUrl": "https://app.example.com/editor", "consoleErrors": [{ "message": "TypeError: x is undefined", "source": "app.js:10" }] }
}type is bug, support, question or feature. Bugs default to high priority.
GET /tickets, GET /tickets/:id, PATCH /tickets/:id, POST /tickets/:id/comments
GET /tickets takes status (open, pending, resolved, closed, active), type, priority, contact_id, q. PATCH takes status, priority, type, title, tags, assigneeId. A comment takes body and optional visibility: "internal"; public comments reach the visitor in the widget and by email.
Feedback
POST /feedback
{ "contact": { "external_id": "user_123" }, "kind": "idea", "title": "Dark mode for exports", "message": "My eyes", "rating": 4 }kind is idea, problem, praise, question or rating.
GET /feedback
Query: kind, status (new, planned, in_progress, done, declined), q, sort=votes.