Back to Docs

Core Concepts

Understand the building blocks of Dispatch Tickets. Every resource in the API maps to one of these concepts.

Overview

Dispatch Tickets is organized around a simple hierarchy. An account owns one or more brands. Each brand is an isolated container of tickets, customers, statuses, and configuration. This makes it easy to support multiple products, clients, or business units from a single API integration.

Account
  └── Brand (isolated ticket container)
        ├── Tickets
        │     ├── Comments
        │     ├── Attachments
        │     └── Watchers
        ├── Customers & Companies
        ├── Statuses (system + custom)
        ├── Categories
        └── Tags

Brands

A brand (also called a workspace) is the top-level container for all ticketing data. Each brand has its own customers, statuses, categories, tags, and email configuration. Data is fully isolated between brands.

Common uses for multiple brands:

  • Separate support queues for different products
  • Client-specific inboxes for agencies
  • Regional support teams (US, EU, APAC)
  • Departmental separation (Support, Sales, Billing)
Create a brand
const brand = await dispatch.brands.create({
  name: 'Acme Support',
  slug: 'acme',       // Used in email addresses
});

console.log(brand.id); // 'br_abc123'

Every API endpoint that accesses tickets, comments, or other data is scoped to a brand via /brands/:brandId/....

Tickets

Tickets are the core resource. Each ticket has a title, status, priority, and optional fields like assignee, customer, category, tags, and custom fields.

Create a ticket
const ticket = await dispatch.tickets.create('br_abc123', {
  title: 'Cannot access my account',
  priority: 'high',
  source: 'email',
  customFields: {
    requesterEmail: '[email protected]',
    requesterName: 'Jane Doe',
    orderId: 'ORD-12345',
    plan: 'enterprise',
  },
});

console.log(ticket.ticketNumber); // 'DT-1042'

Ticket Fields

FieldTypeDescription
titlestringShort summary of the issue
statusstringCurrent status (open, pending, resolved, closed, or custom)
prioritystringlow, normal, high, or urgent
sourcestringChannel origin (api, email, web, slack, etc.)
customFieldsobjectAny JSON data (schemaless)
customFields.requesterEmailstringWho the ticket is from. Creates the contact, or links to an existing one (with requesterName)
companyIdstringThe company the ticket is for. Defaults to the contact's company. See Ticket company
companyobjectInstead of companyId: { sourceOrgId, name }, your own id for the company, created if new

Source Tracking

Every ticket tracks where it came from. The source field records the channel (api, email, web, slack, sms, etc.), sourceId stores the original ID from that channel, and sourceData holds channel-specific metadata as JSON.

Comments

Comments are the conversation thread on a ticket. Each comment has a body (text or HTML), an author, and an author type indicating whether it came from a staff member or customer.

Add a comment
const comment = await dispatch.comments.create('br_abc123', 'tkt_def456', {
  body: 'Thanks for reaching out! Let me look into this.',
  authorType: 'AGENT',
  authorName: 'Support Team',
  authorEmail: '[email protected]',
});

Author Types

  • AGENT - Team member reply (sent to the customer unless it's an internal note)
  • CUSTOMER - Customer reply (triggers notification to assignee)
  • SYSTEM - Automated message (status changes, assignments)

Comments can also include attachments and support threading via the parentId field for nested replies.

Customers & Companies

Customers (contacts) are created automatically when a ticket includes a customFields.requesterEmail that doesn't already exist. You can also create them explicitly.

Customer auto-creation
// Customer is created automatically from ticket data
await dispatch.tickets.create('br_abc123', {
  title: 'Billing question',
  customFields: {
    requesterEmail: '[email protected]',
    requesterName: 'New Customer',
  },
});
// A customer record now exists for [email protected]

Customers can be grouped into companies for B2B use cases. Companies let you see all tickets from an organization in one place and apply shared settings.

Customer Fields

  • email - Primary identifier (unique per brand)
  • name - Display name
  • company - Optional company association
  • customFields - Any additional metadata as JSON

Ticket company

Each ticket also records the company it is for. Usually that is the contact's own company, and you don't need to set anything. When one person works for several companies, an agency or a consultant for example, set the company on the ticket instead. They stay one contact, and each ticket is filed under the right company.

File a ticket under a company
curl -X POST https://api.dispatchtickets.com/v1/brands/br_abc123/tickets \
  -H "Authorization: Bearer sk_live_..." \
  -H "Content-Type: application/json" \
  -d '{
    "title": "Export is stuck",
    "customFields": { "requesterEmail": "[email protected]" },
    "company": { "sourceOrgId": "org_42", "name": "Globex" }
  }'
  • companyId uses a company that already exists in the brand.
  • company.sourceOrgId is your own id for the company. The first ticket creates the company (so include name), and later tickets reuse it.
  • Leave both out to use the contact's company. PATCH a ticket with companyId to move it, or null to clear it.
  • List tickets for one company with ?companyId=cmp_.... Ticket responses include companyId and company; webhook payloads include companyId and companyName.

Statuses

Every brand starts with four system statuses: open, pending, resolved, and closed. You can add custom statuses to match your workflow.

For a deep dive into status configuration and lifecycle management, see the Statuses & Workflows guide.

Open Pending Resolved Closed

Categories & Tags

Categories provide a single, structured classification for each ticket (e.g., "Billing", "Technical", "Feature Request"). A ticket can have one category.

Tags are flexible labels that can be applied to tickets for filtering and organization. A ticket can have multiple tags (e.g., "urgent", "enterprise", "bug").

Using categories and tags
const ticket = await dispatch.tickets.create('br_abc123', {
  title: 'Feature request: Dark mode',
  customerEmail: '[email protected]',
  categoryId: 'cat_billing',
  tagIds: ['tag_feature', 'tag_enterprise'],
});

Both categories and tags are managed per brand. Create them in the dashboard under Settings → Brand → Categories / Tags, or via the API.

Attachments

Attachments are files uploaded to tickets or comments. Files are stored in Cloudflare R2 and accessed via presigned URLs. Files never pass through the API server directly.

Upload Flow

  1. Request an upload URL from the API
  2. Upload the file directly to the presigned URL
  3. Confirm the upload to finalize the attachment
Upload an attachment
// 1. Request upload URL
const upload = await dispatch.attachments.requestUpload('br_abc123', 'tkt_def456', {
  filename: 'screenshot.png',
  contentType: 'image/png',
});

// 2. Upload directly to presigned URL
await fetch(upload.uploadUrl, {
  method: 'PUT',
  body: file,
  headers: { 'Content-Type': 'image/png' },
});

// 3. Confirm the upload
await dispatch.attachments.confirm('br_abc123', 'tkt_def456', upload.id);

Download URLs are also presigned and time-limited. Request a fresh download URL each time rather than caching them.