API reference
REST surface under https://flow.appmate.cloud/api/v1/*. Bearer token auth — issue a token at appmate.cloud/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.
curl https://flow.appmate.cloud/api/v1/apps \ -H "Authorization: Bearer amk_abc12345_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
id (cuid) or its slug. Use whichever you have on hand — the resolver accepts both.Apps
List apps
GET /api/v1/apps
→ 200 OK
{ "apps": [ { "id", "name", "slug", "bundleId", "deepLinkScheme", "logoUrl", "createdAt", "updatedAt" } ] }Get one app
GET /api/v1/apps/{idOrSlug}
→ 200 OK
{ "app": { ... } }Create an app
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
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.
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
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, publishedPUT /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
{
"type": "waitlist",
"intro": {
"title": "Get AppMate Pro early",
"subtitle": "Unlimited apps, waitlist flows, webhooks, A/B testing, higher session caps.",
"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 an access link to the platform once it's 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, stacked one per row
{ "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)
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
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, publishedPUT /api/v1/apps/{idOrSlug}/flows/feedback→ full feedback config (see schema below)POST /api/v1/apps/{idOrSlug}/flows/feedback/publish→ promotes the draftGET /api/v1/apps/{idOrSlug}/feedback/submissions→ paginated submissions (same cursor shape as waitlist signups)
Feedback config schema
{
"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, publishedPUT /api/v1/apps/{idOrSlug}/flows/report→ full report configPOST /api/v1/apps/{idOrSlug}/flows/report/publish→ promotes the draftGET /api/v1/apps/{idOrSlug}/report/submissions→ paginated submissions. Optional?category={id}filter to scope to one bucket.
Report config schema
{
"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
}Contact flow
Hosted contact form at appmate.cloud/contact/{appSlug}. The lowest-friction “reach out” surface — optional name, optional/required email, optional message text.
GET /api/v1/apps/{idOrSlug}/flows/contact→ flow, draft, publishedPUT /api/v1/apps/{idOrSlug}/flows/contact→ full contact config (see schema below)POST /api/v1/apps/{idOrSlug}/flows/contact/publish→ promotes the draftGET /api/v1/apps/{idOrSlug}/contact/submissions→ paginated submissions (same cursor shape as waitlist signups)
Contact config schema
{
"type": "contact",
"intro": {
"title": "Get in touch",
"subtitle": "Tell us how we can help.",
"submitLabel": "Send",
"legal": "Optional small print" // optional
},
"nameField": { // optional
"enabled": true,
"placeholder": "Your name (optional)", // optional
"required": false // optional
},
"emailField": { // optional
"enabled": true,
"placeholder": "you@example.com", // optional
"required": true // optional
},
"messageField": { // optional
"enabled": true,
"placeholder": "What's on your mind?", // optional
"required": false // optional
},
"success": {
"title": "Got it — thanks for writing.",
"body": "We'll reply to your email as soon as we can."
},
"hero": { "theme": "minimal", "accentColor": "#0ea5e9" } // optional
}categories; the public submit endpoint rejects unknown categories with 422.Onboarding flow (web-to-app funnel)
Hosted funnel at appmate.cloud/onboarding/{appSlug} — quiz / info / email-capture steps then an App Store handoff. See the onboarding guide for the config shape and the deferred-handoff flow.
GET /api/v1/apps/{idOrSlug}/flows/onboarding→ flow, draft, publishedPUT /api/v1/apps/{idOrSlug}/flows/onboarding→ full onboarding config; returns{ ok, warnings }POST /api/v1/apps/{idOrSlug}/flows/onboarding/publish→ promotes the draftGET /api/v1/apps/{idOrSlug}/onboarding/submissions→ paginated completions (answers + email + claim status)GET /api/v1/apps/{idOrSlug}/onboarding/submissions.csv→ the same data as CSV
Public (unauthenticated) endpoints the funnel + SDK use: POST /api/public/onboarding/sessions (start), POST /api/public/onboarding/complete (persist answers + mint claim token), and POST /api/public/onboarding/claim (the SDK redeems the claim token for the answers + email on first launch).
Referral program
Share-with-a-friend loop with real install attribution. See the referral guide.
GET /api/v1/apps/{idOrSlug}/flows/referral→ flow, draft, publishedPUT /api/v1/apps/{idOrSlug}/flows/referral→ full referral config; returns{ ok, warnings }POST /api/v1/apps/{idOrSlug}/flows/referral/publish→ promotes the draftGET /api/v1/apps/{idOrSlug}/referrals→ paginated referral graph (optional?status=attributed)GET /api/v1/apps/{idOrSlug}/referrals.csv→ the graph as CSV
Public (unauthenticated) endpoints the app + SDK use: POST /api/public/referral/code(mint a user's share link), POST /api/public/referral/claim-intent (link tap → clipboard token), POST /api/public/referral/attribute (friend's first launch — the reward trigger), and POST /api/public/referral/rewards (referrer claims owed weeks on launch).
Errors
401— missing or invalid Bearer token. Header includesWWW-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.