API Reference

Complete reference for the FrontRow Creator API.

Authentication

All API requests require a Bearer token passed in the Authorization header:

Authorization Header
1Authorization: Bearer fr_live_...

API keys are generated in Settings → API. Each key is scoped to one or more permissions:

  • messages:read — Read conversations and messages
  • messages:write — Send messages
  • posts:write — Create posts
  • subscribers:read — View subscriber list
  • profile: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.

GET/api/v1/me

Returns your creator profile and account stats.

Scope: profile:read

Response
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}
PATCH/api/v1/me

Update your creator profile. All fields are optional.

Scope: profile:read

Request Body
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}
Response
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}
GET/api/v1/conversations

List your DM conversations, ordered by most recent activity.

Scope: messages:read

Response
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}
GET/api/v1/conversations/:id

Retrieve a single conversation by ID.

Scope: messages:read

Response
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}
POST/api/v1/conversations

Start a new conversation with a subscriber.

Scope: messages:write

Request Body
1{
2 "subscriberId": "usr_fan456"
3}
Response
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}
GET/api/v1/conversations/:id/messages

Paginated messages within a conversation.

Scope: messages:read

Query parameters

cursor (optional) — ISO 8601 date string for cursor-based pagination.
Response
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}
POST/api/v1/conversations/:id/messages

Send a message in a conversation as the creator.

Scope: messages:write

Request Body
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.

Response
1{
2 "id": "msg_002",
3 "text": "Thanks for the tip!",
4 "isAiGenerated": true,
5 "createdAt": "2026-04-14T12:01:00Z"
6}
POST/api/v1/conversations/:id/read

Mark all messages in a conversation as read.

Scope: messages:write

Response
1{
2 "success": true,
3 "markedCount": 5
4}
GET/api/v1/posts

List your posts with cursor-based pagination.

Scope: posts:write

Query parameters

cursor (optional) — Cursor for pagination.
Response
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}
GET/api/v1/posts/:id

Retrieve a single post by ID.

Scope: posts:write

Response
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}
POST/api/v1/posts

Create a new post on your profile.

Scope: posts:write

Request Body
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

Response
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}
DELETE/api/v1/posts/:id

Delete a post by ID. This action is irreversible.

Scope: posts:write

Response
1{
2 "success": true
3}
POST/api/v1/upload/image

Upload 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).
Response
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}
POST/api/v1/upload/audio

Upload a single audio file via multipart form data.

Scope: messages:write

Form fields

file — Audio file (MP3, WAV, M4A).
Response
1{
2 "mediaId": "med_aud1"
3}
GET/api/v1/subscribers

List your active subscribers with subscription details.

Scope: subscribers:read

Response
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}
GET/api/v1/followers

List users who follow your profile.

Scope: subscribers:read

Query parameters

cursor (optional) — Cursor for pagination.
Response
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}
GET/api/v1/tips

List tips you have received, ordered by most recent.

Scope: subscribers:read

Query parameters

cursor (optional) — Cursor for pagination.
Response
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}
GET/api/v1/likes

List likes on your posts, ordered by most recent.

Scope: profile:read

Query parameters

cursor (optional) — Cursor for pagination.
Response
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}
GET/api/v1/analytics

Retrieve aggregate analytics for your creator account.

Scope: profile:read

Response
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 message
subscriber.newNew subscription started
subscriber.cancelledSubscription cancelled
tip.receivedYou received a tip
post.likedA fan liked your post

Payload Format

Every webhook delivers a JSON body with the event name, data object, and timestamp.

Webhook Payload
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:

Verify Signature (Node.js)
1const crypto = require('crypto');
2
3const signature = req.headers['x-frontrow-signature'];
4const expected =
5 'sha256=' +
6 crypto
7 .createHmac('sha256', WEBHOOK_SECRET)
8 .update(body)
9 .digest('hex');
10
11if (signature !== expected) {
12 throw new Error('Invalid signature');
13}
POST/api/v1/webhooks/test

Send a test event to a registered webhook endpoint.

Scope: profile:read

Request Body
1{
2 "webhookId": "wh_abc123"
3}
Response
1{
2 "success": true
3}

Building AI Agents

Build autonomous agents that manage conversations on your behalf:

  1. Register a webhook for message.received events
  2. When a fan message arrives, process it through your LLM of choice
  3. 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.

Agent Example
1app.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});