Public REST API

Authentication, rate limits, ticket CRUD, comments, attachments, and error handling.

The FlowMingo public API (/api/v1) lets integrations list, create, update, and delete tickets in a workspace using scoped API keys.

Plan: Public API requires Pro or Enterprise (integrations.public_api). Create keys under Admin → API in the app. Use API Reference → Try It on this docs site to test endpoints interactively.

Getting started in 5 minutes

  1. Create an API key — App → Admin → API → create key with scopes tickets:read and tickets:write. Copy the plaintext key (fm…) — it is shown only once.
  2. Verify the keyGET /me returns your workspace id/name and key scopes.
  3. List ticketsGET /tickets?limit=50 with Authorization: Bearer fm….
  4. Create a ticketPOST /tickets with title, ticketFormId, and optional description.
  5. Add a replyPOST /tickets/{id}/comments with { "content": "…" } (visible in the ticket chat).

Use a production key (app.flowmingo.io) for Try It on this docs site. Keys from dev/staging use a different server secret and return 401 invalid_api_key on production.

Base URL

https://app.flowmingo.io/api/v1

Your workspace is determined by the API key, not the URL path.

Custom domains (e.g. support.yourcompany.com) apply to the web app UI. Integrations should always call https://app.flowmingo.io/api/v1.

Authentication

Authorization: Bearer fmxxxxxxxxxxxxxxxxxxxxxxxx

All endpoints except GET /health and signed attachment downloads require a Bearer token.

GET /me

Returns workspace metadata and the credential bound to the key. Works with any valid key (no scope required) — useful to confirm a write-only key before calling POST /tickets.

curl -s -H "Authorization: Bearer fm…" \
  "https://app.flowmingo.io/api/v1/me"

Scopes

Default-deny. Assign scopes when creating or editing a key in Admin → API.

ScopeAccess
tickets:readGET /tickets, GET /tickets/{id}, GET /attachments/{id}
tickets:writePOST /tickets, PATCH /tickets/{id}, DELETE /tickets/{id}, POST /tickets/{id}/comments

Rate limits

Enforced per workspace and per API key (Upstash). Exceeded limits return HTTP 429 with JSON code: rate_limit_exceeded and header Retry-After.

PlanWorkspace / minAPI key / minWorkspace / day
Basic— (API not available)
Pro1006015,000
Enterprise300120100,000

Response headers: RateLimit-Limit, RateLimit-Remaining, RateLimit-Reset. Implement exponential backoff on 429.

Endpoints (v1)

MethodPathScopeDescription
GET/healthLiveness check
GET/meBearerWorkspace + credential metadata
GET/ticketstickets:readPaginated ticket list (limit, cursor)
POST/ticketstickets:writeCreate ticket
GET/tickets/{id}tickets:readSingle ticket + attachments[]
PATCH/tickets/{id}tickets:writeUpdate status, priority, customFieldData
DELETE/tickets/{id}tickets:writePermanently delete ticket
POST/tickets/{id}/commentstickets:writeAppend public message to ticket chat
GET/attachments/{id}tickets:readDownload file by attachment id (Bearer)
GET/attachments/download?token=…TokenSigned URL from ticket JSON (1h TTL)

Full request/response schemas: API Reference (synced from OpenAPI).

Ticket JSON: status vs workflowStatus

Each ticket has two status concepts:

FieldMeaning
statusLegacy lifecycle column: open, in-progress, resolved, closed
workflowStatusCustom status from the ticket form (id, name, phase) — what agents see in Inbox/Kanban

The app stores the custom status id internally as __workflowStatusId in custom_field_data. API responses expose it as enriched workflowStatus (not in customFieldData).

Updating status via API

Option A — legacy status (simple):

curl -X PATCH -H "Authorization: Bearer fm…" \
  -H "Content-Type: application/json" \
  -d '{"status":"in-progress"}' \
  "https://app.flowmingo.io/api/v1/tickets/FTT-4"

The API picks the matching workflow status for that phase on the ticket form and syncs __workflowStatusId automatically.

Option B — exact custom status:

curl -X PATCH -H "Authorization: Bearer fm…" \
  -H "Content-Type: application/json" \
  -d '{"customFieldData":{"__workflowStatusId":"wfs_abc123"}}' \
  "https://app.flowmingo.io/api/v1/tickets/FTT-4"

Also updates the legacy status column from the workflow phase.

Messages vs description field

  • Initial description — set on POST /tickets via description (creates the first chat message).
  • New replies — use POST /tickets/{id}/comments with { "content": "…" }.
  • Do not PATCH Description in customFieldData — returns 400 with a hint to use comments.

Examples

List tickets

curl -s -H "Authorization: Bearer fm…" \
  "https://app.flowmingo.io/api/v1/tickets?limit=50"

Response: { "tickets": […], "nextCursor": "…", "hasMore": true }. Tickets include enriched ticketForm, category, area, createdBy, assignedTo, and workflowStatus labels.

Create ticket

curl -s -X POST -H "Authorization: Bearer fm…" \
  -H "Content-Type: application/json" \
  -d '{
    "title": "Laptop request",
    "ticketFormId": "tf_abc123",
    "description": "Need a new device",
    "priority": "medium"
  }' \
  "https://app.flowmingo.io/api/v1/tickets"

Returns 201 with { "ticket": { … } }. ticketFormId is the form id from Studio → Ticket forms (must belong to the key's workspace).

Add a public comment

curl -s -X POST -H "Authorization: Bearer fm…" \
  -H "Content-Type: application/json" \
  -d '{"content":"Customer confirmed the fix."}' \
  "https://app.flowmingo.io/api/v1/tickets/FTT-4/comments"

Returns 201 with { "comment": { "id", "content", "createdBy", "createdAt", … } }.

Delete ticket

curl -s -X DELETE -H "Authorization: Bearer fm…" \
  "https://app.flowmingo.io/api/v1/tickets/FTT-4"

Returns { "deleted": true, "id": "FTT-4" }. Deletes messages, history, and storage files. Cannot be undone.

Attachments

GET /tickets/{id} includes an attachments array. File fields in customFieldData also include download metadata.

FieldPurpose
idStable id for Bearer download (recommended)
downloadUrlSigned absolute URL (expires after 1 hour)
pathInternal storage path (debugging)

Bearer download (recommended)

curl -H "Authorization: Bearer fm…" \
  "https://app.flowmingo.io/api/v1/attachments/{id}" \
  --output file.pdf

Add ?inline=1 for inline preview.

Signed URL

Use downloadUrl from the ticket JSON directly (no Bearer). Re-fetch the ticket when the token expires.

Error responses

Errors return JSON: { "error": "…", "code": "…" }.

HTTPcodeTypical cause
400validation_errorInvalid body, blocked status transition, protected field
401invalid_api_keyMissing/expired/revoked key, wrong environment
403insufficient_scopeKey lacks required scope
403Plan does not include Public API
404not_foundTicket/form not in workspace
429rate_limit_exceededPlan rate limit — retry after Retry-After
500internal_errorServer error

Pagination

GET /tickets uses cursor pagination:

  1. First page: ?limit=100 (max 200).
  2. If hasMore is true, pass nextCursor as the cursor query param on the next request.

Product guides

  • Tickets — creating and managing tickets in the UI
  • Quickstart — workspace setup for admins and agents

Interactive Try It for every endpoint: API Reference tab on this site.


Did this page help you?