Every helpdesk I've used follows the same pattern: it's a separate app. Your users email support@company.com, tickets land in Some Other Dashboard™, and your team context-switches between your product and your support tool all day.
It works. But it's not great.
What if support tickets were just... an API primitive? Like Stripe for payments or Twilio for SMS, but for support tickets?
The "We Have An API" Lie
Most helpdesks claim to have APIs. They do—technically. But there's a difference between "we bolted an API onto our app" and "the API is the product."
Here's how you can tell the difference:
Bolted-on API:
- API is read-heavy (pull tickets into your system)
- Webhooks are an afterthought
- You still need their UI for most workflows
- Authentication assumes you're the only consumer
API-first:
- API is the primary interface
- Webhooks are first-class citizens
- Their UI is just a client consuming the same API you do
- Multi-tenant by design
What This Looks Like in Practice
With an API-first ticketing system, creating a ticket is just a POST request:
curl -X POST https://api.dispatchtickets.com/v1/workspaces/ws_xxx/tickets \
-H "Authorization: Bearer your_api_key" \
-H "Content-Type: application/json" \
-d '{
"subject": "Button not working on checkout page",
"description": "User clicked Pay and nothing happened",
"customer_email": "user@example.com",
"priority": "high",
"custom_fields": {
"user_id": "usr_12345",
"page_url": "/checkout",
"browser": "Chrome 120",
"error_log": "TypeError: undefined is not a function"
}
}'
See that custom_fields object? It takes any JSON. No predefined schema. No "please contact sales to add custom fields." Just pass whatever context is relevant.
The Cool Stuff This Enables
Once tickets are an API primitive, you can do things that traditional helpdesks make painful:
1. Automatic Ticket Creation from Errors
// In your error boundary or catch block
window.addEventListener('error', async (event) => {
await fetch('/api/support/tickets', {
method: 'POST',
body: JSON.stringify({
subject: `JS Error: ${event.message}`,
description: event.error?.stack || 'No stack trace',
custom_fields: {
url: window.location.href,
user_id: getCurrentUserId(),
timestamp: new Date().toISOString()
}
})
});
});
Your support team sees the ticket before the user complains.
2. In-App Support That Actually Has Context
Instead of "please describe your issue," you can pre-fill everything:
function SupportButton({ user, currentPage }) {
const openSupport = () => {
openTicketModal({
prefill: {
customer_email: user.email,
custom_fields: {
user_id: user.id,
plan: user.subscription.plan,
page: currentPage,
recent_actions: user.activityLog.slice(-10)
}
}
});
};
return <button onClick={openSupport}>Get Help</button>;
}
No more "what's your account email?" No more "can you send a screenshot?" The context is already there.
3. Build Support Into Your Admin Dashboard
Why make your team learn another app?
// In your existing admin panel
function CustomerDetail({ customerId }) {
const { data: tickets } = useQuery(
['tickets', customerId],
() => fetchTickets({ customer_id: customerId })
);
return (
<div>
<CustomerInfo id={customerId} />
<OrderHistory id={customerId} />
<SupportTickets tickets={tickets} /> {/* Same dashboard */}
</div>
);
}
Your ops team sees customer info, orders, and support tickets in one place. No tab switching.
4. Webhooks for Everything
When a ticket is created, updated, or resolved—you get a webhook. Build whatever you want:
// Your webhook handler
app.post('/webhooks/tickets', (req, res) => {
const { event, ticket } = req.body;
if (event === 'ticket.created' && ticket.priority === 'urgent') {
// Page on-call engineer
pagerDuty.trigger({
summary: ticket.subject,
source: 'support-ticket',
severity: 'high'
});
}
if (event === 'ticket.resolved') {
// Update your CRM
crm.updateContact(ticket.customer_email, {
last_support_interaction: new Date(),
tickets_resolved: increment(1)
});
}
res.sendStatus(200);
});
The Pricing Thing
Traditional helpdesks charge per seat. Five agents? Five seats. But here's the thing—in a modern SaaS, everyone touches support sometimes:
- Engineers triage bugs
- PMs review feature requests
- Founders reply to key accounts
Per-seat pricing punishes this. You either pay $79/month for someone who handles 3 tickets, or you create workarounds (shared logins, forwarding emails, Slack threads about tickets).
API-first systems tend toward usage-based pricing. Pay for tickets, not seats. Everyone can have access.
When Not to Use API-First
To be fair, API-first ticketing isn't for everyone:
- Non-technical teams: If no one can write code, you need a full UI out of the box
- Simple needs: If email-only support with 1-2 agents covers you, a traditional helpdesk is fine
- Existing investment: If you've already built complex workflows in Zendesk, migration has a cost
But if you're building a SaaS product and you have developers on the team? There's no reason your support system should be less flexible than everything else in your stack.
TL;DR
- Traditional helpdesks are apps with APIs bolted on
- API-first ticketing means tickets are the primitive—build whatever you want
- You can embed support in your product, not around it
- Custom fields take any JSON (no schema limits)
- Webhooks let you integrate with everything
- Usage-based pricing means your whole team can have access
I'm building Dispatch Tickets—an API-first ticketing system designed for developers. If you're interested, we're doing early access right now.
Top comments (0)