Beta
Account / Help center

Help center

Support tickets through the API.

Support tickets and feature requests, addressed by their public short_id. Ticket creation is web-app-only — the API reads, replies, and closes what exists.

List tickets

GET/help/support/ticketsapi.daily-tube.com/v1

Your tickets, most recently updated first (complete list — no pagination), each with the last message preview and unread count:

{
"data": {
"items": [
{
"short_id": "ab12cd34",
"category": "bug_report",
"subject": "Briefing didn't arrive",
"status": "open",
"created_at": "2026-10-25T09:00:00Z",
"updated_at": "2026-10-26T08:00:00Z",
"last_message": { "text": "Thanks — looking into it.", "sender": "support", "sent_at": "2026-10-26T08:00:00Z" },
"unread_count": 1
}
]
},
"request_id": "5f0c…"
}

last_message.sender is you or support; unread_count counts unread support replies. last_message is absent on a ticket with no replies yet.

Read a ticket

GET/help/support/tickets/{short_id}api.daily-tube.com/v1

Full thread — messages oldest first, with sender roles, timestamps, read flags, closed_at when resolved, and attachments as freshly minted short-lived URLs (15 minutes — re-fetch the thread for new ones; never store them). Messages whose send had rejected files also carry failed_attachments (persisted name + reason pairs — the message itself sent):

{
"data": {
"short_id": "ab12cd34",
"category": "bug_report",
"subject": "Briefing didn't arrive",
"status": "open",
"created_at": "2026-10-25T09:00:00Z",
"updated_at": "2026-10-26T08:00:00Z",
"messages": [
{ "sender": "you", "body": "My Monday briefing never arrived.", "sent_at": "2026-10-25T09:00:00Z", "read": true, "attachments": [] },
{ "sender": "support", "body": "Here's what we found —", "sent_at": "2026-10-26T08:00:00Z", "read": false, "attachments": [ { "name": "screenshot.png", "url": "https://…supabase.co/storage/v1/object/sign/support-attachments/…?token=…" } ] }
]
},
"request_id": "5f0c…"
}

Reply — with files, in one request

POST/help/support/tickets/{short_id}api.daily-tube.com/v1

The reply action carries the message text and up to 5 files in one multipart/form-data request — the service validates and uploads them for you; no separate upload step exists. Files: ≤5 per message, 8MB total across the message, types png/jpeg/gif/webp/pdf/txt/zip — validated by content, not extension.

10 messages / 10 minutes per user, across all tickets — 429 rate_limit.support_messages with Retry-After when you hit it. This includes ticket creation (web-app side); if a ticket's first message would exceed the cap, creation itself is refused with the same error — retry in a minute.

Request (multipart)

PartTypeRules
actionfieldreply
messagefield1–5000 characters after trim
filesfile ×N≤5 files, 8MB total, content-validated
curl -X POST https://api.daily-tube.com/v1/help/support/tickets/ab12cd34 \
-H "Authorization: Bearer dtk_YOUR_KEY" \
-F action=reply \
-F message="Still seeing this after the restart." \
-F "[email protected]"

Response — always sends, failures reported

The message sends even if some files fail validation — the response lists them:

{
"data": {
"message": { "sender": "you", "body": "Still seeing this after the restart.", "sent_at": "2026-10-26T10:00:00Z", "read": false },
"ticket": { "short_id": "ab12cd34", "status": "open", "updated_at": "2026-10-26T10:00:00Z" },
"failed_attachments": [ { "name": "dump.zip", "reason": "Attachment budget exceeded (8MB total per message)" } ]
},
"request_id": "5f0c…"
}

A JSON-only variant exists for text-only replies or referencing files your account already sent: pass attachment_paths (paths under your own attachment folder — the server scopes them to you; foreign paths are rejected) instead of files. More than 5 distinct declared paths is a hard 400 attachments.too_many.

Other ticket actions

Same endpoint, JSON:

ActionBodyEffectResponse
mark-read{ "action": "mark-read" }Clears the unread flag on support replies200 — { ticket }
close{ "action": "close" }Closes the ticket200 — { ticket } (with closed_at)

Feature requests

  • GET /help/feature-requests — your submissions, newest first: items of { short_id, i_want, so_that, status, created_at }.
  • GET /help/feature-requests/{short_id} — one request in the same shape.
  • POST /help/feature-requests/{short_id} — { "action": "withdraw" } only (open requests; 400 help.request_not_withdrawable otherwise) → 200 { short_id, status: "withdrawn" }.

Feature-request creation and discussion happen in the web app — the API exposes listing and withdrawal only.

Errors

Every error returns the same body — code is the stable machine string, message is human. The full registry of every code lives on Errors.

{
"error": { "code": "help.ticket_closed", "message": "Cannot add messages to a closed ticket" },
"request_id": "5f0c…"
}
CodeStatusWhen
help.ticket_not_found / request_not_found404Foreign or unknown id
help.ticket_closed400Reply or close on a closed ticket
help.request_not_withdrawable400Withdraw on a non-open request
attachments.invalid400Malformed attachment paths; request over the 8MB budget
attachments.too_many400More than 5 distinct declared paths
rate_limit.support_messages42910 messages / 10 min per user (+ Retry-After)
validation.invalid_input400Unknown action; empty/oversized message

Every keyed endpoint on this page shares the per-key budget — Rate limits.