API reference

REST surface under https://flow.appmate.cloud/api/v1/*. Bearer token auth — issue a token at flow.appmate.cloud/admin/settings/api-tokens. The MCP server in fil-technology/appmate-mcp is a typed wrapper around this surface — under the hood it calls these same endpoints.

Authentication

Every request needs an Authorization: Bearer amk_... header. Tokens look like amk_<8-char prefix>_<32-char secret> and are shown once on creation — we store only the bcrypt hash. Revoke from the dashboard.

bash
curl https://flow.appmate.cloud/api/v1/apps \
  -H "Authorization: Bearer amk_abc12345_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
Paths accept either the app's id (cuid) or its slug. Use whichever you have on hand — the resolver accepts both.

Apps

List apps

http
GET /api/v1/apps

→ 200 OK
{ "apps": [ { "id", "name", "slug", "bundleId", "deepLinkScheme", "logoUrl", "createdAt", "updatedAt" } ] }

Get one app

http
GET /api/v1/apps/{idOrSlug}

→ 200 OK
{ "app": { ... } }

Create an app

http
POST /api/v1/apps
Content-Type: application/json

{
  "name": "QuakeMate",
  "slug": "quakemate",         // optional; auto-slugged from name if omitted
  "bundleId": "com.example.quakemate",   // optional
  "deepLinkScheme": "quakemate",         // optional
  "logoUrl": "https://example.com/logo.png" // optional
}

→ 201 Created
{ "app": { ... } }

Cancel flow

Get the flow

http
GET /api/v1/apps/{idOrSlug}/flows/cancel

→ 200 OK
{
  "flow":      { "id", "type", "slug", "status", "currentVersionId" },
  "draft":     { "versionNumber", "config": { ... } },
  "published": { "versionNumber", "publishedAt", "config": { ... } } | null
}

Update the draft

Body is the full config JSON (not wrapped in a config key). Validates with the same Zod schema the editor uses; rejects with 422 on any issue. See the deep-link contract for the action shapes.

http
PUT /api/v1/apps/{idOrSlug}/flows/cancel
Content-Type: application/json

{
  "type": "cancel",
  "intro": { "title": "Before you go", "subtitle": "…", "primaryButton": "Continue", "secondaryButton": "Skip" },
  "reasonScreen": { "title": "Why?", "subtitle": "", "reasons": [ { "id": "too_expensive", "label": "Too expensive", "iconName": "DollarSign" } ] },
  "responses": { "too_expensive": { "title": "20% off?", "body": "…", "primaryButton": { "label": "Claim", "action": "open_offer", "offerId": "save_20" } } }
}

→ 200 OK   { "ok": true }
→ 422      { "error": "<paths>: <messages>" }

Publish the draft

http
POST /api/v1/apps/{idOrSlug}/flows/cancel/publish

→ 200 OK   { "ok": true, "version": 4 }
→ 422      { "error": "draft is invalid: ..." }

Waitlist flow

Same shape conventions as cancel — GET / PUT / POST publish at /flows/waitlist. The PUT body is the full waitlist config; the optional heroblock drives the public landing page's visual treatment.

  • GET /api/v1/apps/{idOrSlug}/flows/waitlist → flow, draft, published
  • PUT /api/v1/apps/{idOrSlug}/flows/waitlist → full waitlist config (see schema below)
  • POST /api/v1/apps/{idOrSlug}/flows/waitlist/publish → promotes the draft

Waitlist config schema

json
{
  "type": "waitlist",
  "intro": {
    "title": "Get AppMate Pro early",
    "subtitle": "Unlimited apps, custom domains, webhooks…",
    "emailPlaceholder": "you@company.com",
    "submitLabel": "Notify me",
    "legal": "One email when Pro launches."          // optional
  },
  "success": {
    "title": "You're on the list.",
    "body": "We'll email you the moment Pro is ready.",
    "ctaLabel": "Back to the dashboard",             // optional, paired with ctaUrl
    "ctaUrl":   "https://flow.appmate.cloud/login"   // both-or-neither
  },

  // EVERYTHING below is optional — omit `hero` for the minimal look.
  "hero": {
    "theme": "gradient",        // minimal | gradient | dark | side_by_side
    "eyebrow": "Coming soon",   // chip above title (max 40 chars)
    "accentColor": "#7c3aed",   // hex; tints button + eyebrow + gradient blob
    "showCount": true,          // shows "{N} on the waitlist" pill (hides if <3)
    "heroImage": "https://…",   // optional hero image URL
    "bullets": [                // 0–5 value-prop cards
      { "icon": "📈", "title": "Unlimited apps", "body": "Across your portfolio." },
      { "icon": "🪝", "title": "Webhooks + API" }
    ]
  },

  "templateId": "feature_tease" // optional analytics tag
}

Theme picker guide: minimal = clean white card (default, good for B2B). gradient = pastel blobs + accent (marketing launch). dark = dark hero + accent glow (product reveal). side_by_side = 2-column desktop layout (story left, form right), collapses to minimal on phone.

Starter templates that pre-fill the whole hero block: minimal_email_only, feature_tease, launching_soon, pro_upsell, private_beta, early_access_referral. Browse them at /examples?kind=waitlist.

Waitlist signups

List signups (paginated)

http
GET /api/v1/apps/{idOrSlug}/waitlist/signups?limit=50&cursor=<id>

→ 200 OK
{
  "signups":   [ { "id", "email", "source", "country", "userAgent", "createdAt" } ],
  "nextCursor": "<id>" | null
}

Pass nextCursor back as cursor on the next call. Page size is capped at 200. country is a 2-letter ISO code (when the edge sent one) and userAgent is the raw UA string — both nullable. Captured at submit time for analytics; the raw client IP is never stored.

Export as CSV

http
GET /api/v1/apps/{idOrSlug}/waitlist/signups.csv

→ 200 OK
Content-Type: text/csv

id,email,source,country,user_agent,createdAt
…

Feedback flow

Hosted feedback form at appmate.cloud/feedback/{appSlug}. Optional 1–5 star rating + free-text message + optional reply email.

  • GET /api/v1/apps/{idOrSlug}/flows/feedback → flow, draft, published
  • PUT /api/v1/apps/{idOrSlug}/flows/feedback → full feedback config (see schema below)
  • POST /api/v1/apps/{idOrSlug}/flows/feedback/publish → promotes the draft
  • GET /api/v1/apps/{idOrSlug}/feedback/submissions → paginated submissions (same cursor shape as waitlist signups)

Feedback config schema

json
{
  "type": "feedback",
  "intro": {
    "title":              "Send us feedback",
    "subtitle":           "We read every message.",
    "messagePlaceholder": "What's on your mind?",
    "submitLabel":        "Send feedback",
    "legal":              "Optional small print"        // optional
  },
  "rating": {                                            // optional
    "enabled":  true,
    "prompt":   "How would you rate your experience?",   // optional
    "required": false                                    // optional
  },
  "emailField": {                                        // optional
    "enabled":     true,
    "placeholder": "you@example.com (optional)",         // optional
    "required":    false                                 // optional
  },
  "success": {
    "title":   "Thanks — we got it.",
    "body":    "Your feedback is in front of the team."
  },
  "hero": { "theme": "minimal", "accentColor": "#7c3aed" } // optional
}

Report flow

Hosted bug/abuse/spam report form at appmate.cloud/report/{appSlug}. Required category picker (1–10 entries) + free-text message + optional reply email.

  • GET /api/v1/apps/{idOrSlug}/flows/report → flow, draft, published
  • PUT /api/v1/apps/{idOrSlug}/flows/report → full report config
  • POST /api/v1/apps/{idOrSlug}/flows/report/publish → promotes the draft
  • GET /api/v1/apps/{idOrSlug}/report/submissions → paginated submissions. Optional ?category={id} filter to scope to one bucket.

Report config schema

json
{
  "type": "report",
  "intro": {
    "title":              "Report an issue",
    "subtitle":           "Tell us what went wrong.",
    "messagePlaceholder": "What happened?",
    "submitLabel":        "Submit report",
    "legal":              "Optional small print"        // optional
  },
  "categories": [                                       // 1–10 entries
    { "id": "bug",     "label": "Bug or crash",      "emoji": "🐞" },
    { "id": "abuse",   "label": "Harassment or abuse","emoji": "🚫" },
    { "id": "spam",    "label": "Spam",               "emoji": "🧹" },
    { "id": "privacy", "label": "Privacy concern",    "emoji": "🔒" },
    { "id": "other",   "label": "Something else",     "emoji": "💬" }
  ],
  "emailField": {                                       // optional
    "enabled":     true,
    "placeholder": "you@example.com (optional)",
    "required":    false
  },
  "success": {
    "title": "Report received.",
    "body":  "Thanks for letting us know."
  },
  "hero": { "theme": "minimal", "accentColor": "#dc2626" } // optional
}
Both feedback & report submission endpoints accept anonymous submissions by default — the email field is opt-in. Category ids on the report flow must match an entry in categories; the public submit endpoint rejects unknown categories with 422.

Errors

  • 401 — missing or invalid Bearer token. Header includes WWW-Authenticate: Bearer realm="AppMate API".
  • 404 — unknown {appId}.
  • 409 — slug collision on create.
  • 422 — config failed validation. Body { error: "..." } describes the failing paths.

Versioning

This is /api/v1/*. Breaking changes will land at /api/v2/*; v1 stays put. Tokens work against either version when v2 ships.