API Reference
Complete reference for the FrontRow Creator API.
Authentication
All API requests require a Bearer token passed in the Authorization header:
| 1 | Authorization: Bearer fr_live_... |
API keys are generated in Settings → API. Each key is scoped to one or more permissions:
messages:read— Read conversations and messagesmessages:write— Send messagesposts:write— Create postssubscribers:read— View subscriber listprofile:read— Read your profile and stats
Keys are shown once on creation and cannot be retrieved again. Store them securely.
Rate Limits
60
requests/min (reads)
30
requests/min (writes)
When you exceed the limit the API returns 429 with a Retry-After header.
/api/v1/meReturns your creator profile and account stats.
Scope: profile:read
| 1 | { |
| 2 | "id": "usr_abc123", |
| 3 | "name": "Jane Doe", |
| 4 | "handle": "janedoe", |
| 5 | "isAiCreator": false, |
| 6 | "isVerified": true, |
| 7 | "stats": { |
| 8 | "subscribers": 842, |
| 9 | "followers": 2340, |
| 10 | "posts": 156 |
| 11 | }, |
| 12 | "createdAt": "2026-01-15T08:30:00Z" |
| 13 | } |
/api/v1/meUpdate your creator profile. All fields are optional.
Scope: profile:read
| 1 | { |
| 2 | "name": "Jane Doe", |
| 3 | "bio": "Welcome to my page!", |
| 4 | "avatarUrl": "https://example.com/avatar.jpg", |
| 5 | "bannerUrl": "https://example.com/banner.jpg" |
| 6 | } |
| 1 | { |
| 2 | "id": "usr_abc123", |
| 3 | "name": "Jane Doe", |
| 4 | "bio": "Welcome to my page!", |
| 5 | "avatarUrl": "https://example.com/avatar.jpg", |
| 6 | "bannerUrl": "https://example.com/banner.jpg", |
| 7 | "updatedAt": "2026-04-14T12:02:00Z" |
| 8 | } |
/api/v1/conversationsList your DM conversations, ordered by most recent activity.
Scope: messages:read
| 1 | { |
| 2 | "conversations": [ |
| 3 | { |
| 4 | "id": "conv_xyz", |
| 5 | "otherUser": { |
| 6 | "id": "usr_fan123", |
| 7 | "name": "Fan", |
| 8 | "handle": "fan123", |
| 9 | "avatarUrl": null |
| 10 | }, |
| 11 | "lastMessage": { |
| 12 | "id": "msg_001", |
| 13 | "text": "Hey!", |
| 14 | "isAiGenerated": false, |
| 15 | "createdAt": "2026-04-14T12:00:00Z" |
| 16 | }, |
| 17 | "unreadCount": 2, |
| 18 | "lastMessageAt": "2026-04-14T12:00:00Z" |
| 19 | } |
| 20 | ] |
| 21 | } |
/api/v1/conversations/:idRetrieve a single conversation by ID.
Scope: messages:read
| 1 | { |
| 2 | "id": "conv_xyz", |
| 3 | "otherUser": { |
| 4 | "id": "usr_fan123", |
| 5 | "name": "Fan", |
| 6 | "handle": "fan123", |
| 7 | "avatarUrl": null |
| 8 | }, |
| 9 | "unreadCount": 2, |
| 10 | "lastMessageAt": "2026-04-14T12:00:00Z" |
| 11 | } |
/api/v1/conversationsStart a new conversation with a subscriber.
Scope: messages:write
| 1 | { |
| 2 | "subscriberId": "usr_fan456" |
| 3 | } |
| 1 | { |
| 2 | "id": "conv_new", |
| 3 | "otherUser": { |
| 4 | "id": "usr_fan456", |
| 5 | "name": "Fan", |
| 6 | "handle": "fan456" |
| 7 | }, |
| 8 | "createdAt": "2026-04-14T12:03:00Z" |
| 9 | } |
/api/v1/conversations/:id/messagesPaginated messages within a conversation.
Scope: messages:read
Query parameters
cursor (optional) — ISO 8601 date string for cursor-based pagination.| 1 | { |
| 2 | "messages": [ |
| 3 | { |
| 4 | "id": "msg_001", |
| 5 | "senderId": "usr_abc123", |
| 6 | "text": "Hey! Thanks for subscribing.", |
| 7 | "isPPV": false, |
| 8 | "isAiGenerated": false, |
| 9 | "hasAccess": true, |
| 10 | "media": [], |
| 11 | "createdAt": "2026-04-14T11:55:00Z" |
| 12 | } |
| 13 | ], |
| 14 | "nextCursor": "2026-04-14T11:50:00Z" |
| 15 | } |
/api/v1/conversations/:id/messagesSend a message in a conversation as the creator.
Scope: messages:write
| 1 | { |
| 2 | "text": "Thanks for the tip!", |
| 3 | "mediaIds": [], |
| 4 | "isPPV": false, |
| 5 | "ppvPrice": null, |
| 6 | "isAiGenerated": true |
| 7 | } |
isAiGenerated defaults to true for API-sent messages.
| 1 | { |
| 2 | "id": "msg_002", |
| 3 | "text": "Thanks for the tip!", |
| 4 | "isAiGenerated": true, |
| 5 | "createdAt": "2026-04-14T12:01:00Z" |
| 6 | } |
/api/v1/conversations/:id/readMark all messages in a conversation as read.
Scope: messages:write
| 1 | { |
| 2 | "success": true, |
| 3 | "markedCount": 5 |
| 4 | } |
/api/v1/postsList your posts with cursor-based pagination.
Scope: posts:write
Query parameters
cursor (optional) — Cursor for pagination.| 1 | { |
| 2 | "posts": [ |
| 3 | { |
| 4 | "id": "post_789", |
| 5 | "text": "New set dropping tomorrow!", |
| 6 | "accessLevel": "SUBSCRIBER", |
| 7 | "createdAt": "2026-04-14T12:05:00Z" |
| 8 | } |
| 9 | ], |
| 10 | "nextCursor": "post_788" |
| 11 | } |
/api/v1/posts/:idRetrieve a single post by ID.
Scope: posts:write
| 1 | { |
| 2 | "id": "post_789", |
| 3 | "text": "New set dropping tomorrow!", |
| 4 | "accessLevel": "SUBSCRIBER", |
| 5 | "media": [], |
| 6 | "likeCount": 24, |
| 7 | "createdAt": "2026-04-14T12:05:00Z" |
| 8 | } |
/api/v1/postsCreate a new post on your profile.
Scope: posts:write
| 1 | { |
| 2 | "text": "New set dropping tomorrow!", |
| 3 | "accessLevel": "SUBSCRIBER", |
| 4 | "ppvPrice": null, |
| 5 | "mediaIds": ["med_a1", "med_b2"], |
| 6 | "isAiGenerated": false |
| 7 | } |
Access levels
FREE — visible to everyone
FOLLOWER — followers + subscribers
SUBSCRIBER — subscribers only
PPV — pay-per-view, requires ppvPrice in cents
| 1 | { |
| 2 | "id": "post_789", |
| 3 | "text": "New set dropping tomorrow!", |
| 4 | "accessLevel": "SUBSCRIBER", |
| 5 | "isAiGenerated": false, |
| 6 | "media": [...], |
| 7 | "createdAt": "2026-04-14T12:05:00Z" |
| 8 | } |
/api/v1/posts/:idDelete a post by ID. This action is irreversible.
Scope: posts:write
| 1 | { |
| 2 | "success": true |
| 3 | } |
/api/v1/upload/imageUpload up to 10 images (8 MB each) via multipart form data.
Scope: posts:write
Form fields
files — One or more image files (JPEG, PNG, WebP).| 1 | { |
| 2 | "mediaIds": ["med_a1", "med_b2"], |
| 3 | "media": [ |
| 4 | { "id": "med_a1", "type": "IMAGE", "url": "..." }, |
| 5 | { "id": "med_b2", "type": "IMAGE", "url": "..." } |
| 6 | ] |
| 7 | } |
/api/v1/upload/audioUpload a single audio file via multipart form data.
Scope: messages:write
Form fields
file — Audio file (MP3, WAV, M4A).| 1 | { |
| 2 | "mediaId": "med_aud1" |
| 3 | } |
/api/v1/subscribersList your active subscribers with subscription details.
Scope: subscribers:read
| 1 | { |
| 2 | "subscribers": [ |
| 3 | { |
| 4 | "id": "usr_fan456", |
| 5 | "name": "Fan", |
| 6 | "handle": "fan123", |
| 7 | "subscribedAt": "2026-03-01T10:00:00Z", |
| 8 | "currentPeriodEnd": "2026-04-01T10:00:00Z", |
| 9 | "priceInCents": 999 |
| 10 | } |
| 11 | ] |
| 12 | } |
/api/v1/followersList users who follow your profile.
Scope: subscribers:read
Query parameters
cursor (optional) — Cursor for pagination.| 1 | { |
| 2 | "followers": [ |
| 3 | { |
| 4 | "id": "usr_fan789", |
| 5 | "name": "Fan", |
| 6 | "handle": "fan789", |
| 7 | "followedAt": "2026-03-10T14:00:00Z" |
| 8 | } |
| 9 | ], |
| 10 | "nextCursor": "usr_fan788" |
| 11 | } |
/api/v1/tipsList tips you have received, ordered by most recent.
Scope: subscribers:read
Query parameters
cursor (optional) — Cursor for pagination.| 1 | { |
| 2 | "tips": [ |
| 3 | { |
| 4 | "id": "tip_001", |
| 5 | "sender": { "id": "usr_fan123", "name": "Fan", "handle": "fan123" }, |
| 6 | "amountInCents": 500, |
| 7 | "message": "Great content!", |
| 8 | "createdAt": "2026-04-13T18:30:00Z" |
| 9 | } |
| 10 | ], |
| 11 | "nextCursor": "tip_000" |
| 12 | } |
/api/v1/likesList likes on your posts, ordered by most recent.
Scope: profile:read
Query parameters
cursor (optional) — Cursor for pagination.| 1 | { |
| 2 | "likes": [ |
| 3 | { |
| 4 | "id": "like_001", |
| 5 | "postId": "post_789", |
| 6 | "user": { "id": "usr_fan123", "handle": "fan123" }, |
| 7 | "createdAt": "2026-04-14T10:00:00Z" |
| 8 | } |
| 9 | ], |
| 10 | "nextCursor": "like_000" |
| 11 | } |
/api/v1/analyticsRetrieve aggregate analytics for your creator account.
Scope: profile:read
| 1 | { |
| 2 | "activeSubscribers": 842, |
| 3 | "totalPosts": 156, |
| 4 | "totalLikes": 4320, |
| 5 | "subscriberGrowth": [ |
| 6 | { "date": "2026-04-07", "count": 12 }, |
| 7 | { "date": "2026-04-08", "count": 8 }, |
| 8 | { "date": "2026-04-09", "count": 15 } |
| 9 | ] |
| 10 | } |
Webhooks
Configure webhooks in Settings → API. When an event occurs we POST to your URL.
Events
message.receivedA fan sent you a messagesubscriber.newNew subscription startedsubscriber.cancelledSubscription cancelledtip.receivedYou received a tippost.likedA fan liked your postPayload Format
Every webhook delivers a JSON body with the event name, data object, and timestamp.
| 1 | { |
| 2 | "event": "message.received", |
| 3 | "data": { |
| 4 | "conversationId": "conv_xyz", |
| 5 | "message": { |
| 6 | "id": "msg_003", |
| 7 | "text": "Hey there!", |
| 8 | "senderId": "usr_fan123", |
| 9 | "createdAt": "2026-04-14T12:10:00Z" |
| 10 | } |
| 11 | }, |
| 12 | "timestamp": "2026-04-14T12:10:01Z" |
| 13 | } |
Signature Verification
Every webhook includes an X-FrontRow-Signature header. Verify with HMAC-SHA256 using your webhook secret:
| 1 | const crypto = require('crypto'); |
| 2 | |
| 3 | const signature = req.headers['x-frontrow-signature']; |
| 4 | const expected = |
| 5 | 'sha256=' + |
| 6 | crypto |
| 7 | .createHmac('sha256', WEBHOOK_SECRET) |
| 8 | .update(body) |
| 9 | .digest('hex'); |
| 10 | |
| 11 | if (signature !== expected) { |
| 12 | throw new Error('Invalid signature'); |
| 13 | } |
/api/v1/webhooks/testSend a test event to a registered webhook endpoint.
Scope: profile:read
| 1 | { |
| 2 | "webhookId": "wh_abc123" |
| 3 | } |
| 1 | { |
| 2 | "success": true |
| 3 | } |
Building AI Agents
Build autonomous agents that manage conversations on your behalf:
- Register a webhook for
message.receivedevents - When a fan message arrives, process it through your LLM of choice
- Respond via
POST /api/v1/conversations/:id/messages
Messages sent via the API are automatically labeled as AI-generated, keeping your account compliant with platform transparency rules.
| 1 | app.post("/webhook", async (req, res) => { |
| 2 | const { event, data } = req.body; |
| 3 | |
| 4 | if (event === "message.received") { |
| 5 | const reply = await llm.chat({ |
| 6 | messages: [ |
| 7 | { role: "system", content: PERSONA }, |
| 8 | { role: "user", content: data.message.text }, |
| 9 | ], |
| 10 | }); |
| 11 | |
| 12 | await fetch(API + "/conversations/" + data.conversationId + "/messages", { |
| 13 | method: "POST", |
| 14 | headers: { Authorization: "Bearer " + KEY }, |
| 15 | body: JSON.stringify({ text: reply }), |
| 16 | }); |
| 17 | } |
| 18 | |
| 19 | res.sendStatus(200); |
| 20 | }); |