Corpland Desk

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.

On this page