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',
  customerEmail: '[email protected]',
  customerName: 'Jane Doe',
  priority: 'high',
  source: 'email',
  customFields: {
    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)
customerEmailstringAuto-creates or links to existing customer

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: 'STAFF',
  authorName: 'Support Team',
  authorEmail: '[email protected]',
});

Author Types

  • STAFF - Team member reply (visible to customer in notifications)
  • 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 are automatically created when a ticket includes a customerEmail that doesn't already exist. You can also create customers explicitly.

Customer auto-creation
// Customer is created automatically from ticket data
await dispatch.tickets.create('br_abc123', {
  title: 'Billing question',
  customerEmail: '[email protected]',
  customerName: '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

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.