Skip to content

Zorbit API Docs

/api/v1

All API endpoints require authentication via Clerk. The user’s session is automatically validated using @clerk/nextjs/server. Include the authentication token in the Authorization header:

Authorization: Bearer <token>

Authentication Flow:

  1. Users authenticate through Clerk
  2. Clerk provides a session token
  3. Include the token in all API requests
  4. The API validates the token and extracts user information

Retrieve the authenticated user’s profile information.

Endpoint: GET /api/v1/user

Headers:

  • Authorization: Bearer <token> (required)
  • X-Request-ID: <uuid> (optional, for request tracking)

Response Headers:

  • X-RateLimit-Limit: 1000
  • X-RateLimit-Remaining: 999
  • X-RateLimit-Reset: 1640995200
  • X-Request-ID: <uuid>

Response:

{
"id": "user_xxx",
"firstName": "John",
"lastName": "Doe",
"email": "john.doe@example.com",
"bio": "Software Engineer",
"imageUrl": "https://...",
"imageKey": "user/xxx"
}

Status Codes:

  • 200 - Success
  • 401 - Unauthorized
  • 404 - User not found
  • 429 - Rate limit exceeded
  • 500 - Internal server error

Update the authenticated user’s profile information.

Endpoint: PUT /api/v1/user

Headers:

  • Authorization: Bearer <token> (required)
  • Content-Type: application/json (required)
  • Idempotency-Key: <uuid> (optional, for idempotent requests)

Request Body:

{
"name": "John Doe",
"imageKey": "user/xxx"
}

Response:

{
"id": "user_xxx",
"firstName": "John",
"lastName": "Doe",
"email": "john.doe@example.com",
"imageUrl": "https://...",
"imageKey": "user/xxx"
}

Status Codes:

  • 200 - Success
  • 400 - Bad request (validation error)
  • 401 - Unauthorized
  • 409 - Conflict (idempotency key already used)
  • 429 - Rate limit exceeded
  • 500 - Internal server error

Generate LinkedIn posts using AI based on a prompt and optional persona.

Endpoint: POST /api/v1/post/generate

Headers:

  • Authorization: Bearer <token> (required)
  • Content-Type: application/json (required)
  • Idempotency-Key: <uuid> (recommended)

Request Body:

{
"prompt": "Write a post about AI in healthcare",
"personaId": "persona_xxx",
"selectedPosts": ["post_id_1", "post_id_2"],
"threadId": "thread_xxx",
"isFirstMessage": false,
"contentLength": "medium"
}

Response:

{
"posts": [
{
"id": "post_xxx",
"content": {
"text": "Generated post content..."
},
"timestamp": "2024-01-01T00:00:00.000Z",
"personaId": "persona_xxx",
"contentLength": "medium"
}
],
"partialSuccess": false,
"totalRequested": 2,
"totalGenerated": 2,
"personaUsed": {
"id": "persona_xxx",
"name": "Tech Professional",
"isSystem": false
},
"threadId": "thread_xxx",
"generationTime": 3.25
}

Status Codes:

  • 200 - Success
  • 400 - Bad request (missing threadId or invalid parameters)
  • 401 - Unauthorized
  • 402 - Payment required (quota exhausted)
  • 409 - Conflict (idempotency key already used)
  • 429 - Rate limit exceeded
  • 500 - Internal server error

Quota: Consumes post_generation quota (1 unit per post generated)

Rate Limits: 100 requests per hour per user


Retrieve posts with pagination, filtering, and sorting.

Endpoint: GET /api/v1/post

Query Parameters:

  • page (number, default: 1, min: 1) - Page number
  • limit (number, default: 10, min: 1, max: 100) - Items per page
  • type (string, optional) - Filter by post type
  • personaId (string, optional) - Filter by persona ID
  • scheduledOnly (boolean, optional) - Only return scheduled posts
  • postedOnly (boolean, optional) - Only return posted content
  • sort (string, default: “createdAt:desc”) - Sort format: “field:direction”
    • Fields: createdAt, scheduledOn, linkedinPostedAt
    • Directions: asc, desc

Response:

{
"posts": [
{
"id": "post_xxx",
"content": "Post content...",
"type": "post",
"personaId": "persona_xxx",
"createdAt": "2024-01-01T00:00:00.000Z",
"scheduledOn": null,
"isPostedOnLinkedin": false,
"linkedinUrl": null
}
],
"hasMore": true,
"totalCount": 50,
"page": 1,
"totalPages": 5
}

Status Codes:

  • 200 - Success
  • 400 - Bad request (invalid query parameters)
  • 401 - Unauthorized
  • 429 - Rate limit exceeded
  • 500 - Internal server error

Rate Limits: 1000 requests per hour per user


Create a new post manually.

Endpoint: POST /api/v1/post

Headers:

  • Authorization: Bearer <token> (required)
  • Content-Type: application/json (required)
  • Idempotency-Key: <uuid> (recommended)

Request Body:

{
"content": "Post content here",
"type": "post",
"personaId": "persona_xxx",
"isSaved": false,
"image": "data:image/png;base64,..."
}

Response:

{
"id": "post_xxx",
"content": "Post content here",
"type": "post",
"userId": "user_xxx",
"createdAt": "2024-01-01T00:00:00.000Z"
}

Status Codes:

  • 201 - Created
  • 400 - Bad request (missing content or validation error)
  • 401 - Unauthorized
  • 409 - Conflict (idempotency key already used)
  • 413 - Payload too large (image size limit exceeded)
  • 429 - Rate limit exceeded
  • 500 - Internal server error

Rate Limits: 500 requests per hour per user


Retrieve a specific post by ID.

Endpoint: GET /api/v1/post/[id]

Response:

{
"id": "post_xxx",
"title": "Post Title",
"content": "Post content",
"imageUrl": "https://...",
"imageKey": "post/xxx",
"published": false,
"isPostedOnLinkedin": false,
"linkedinUrl": null,
"linkedinPostedAt": null,
"type": "post",
"source": "manual",
"createdAt": "2024-01-01T00:00:00.000Z",
"updatedAt": "2024-01-01T00:00:00.000Z",
"isLiked": false,
"isDisliked": false,
"isSaved": false,
"userId": "user_xxx",
"personaId": "persona_xxx"
}

Status Codes:

  • 200 - Success
  • 401 - Unauthorized
  • 403 - Forbidden (not post owner)
  • 404 - Post not found
  • 429 - Rate limit exceeded
  • 500 - Internal server error

Update an existing post.

Endpoint: PUT /api/v1/post/[id]

Headers:

  • Authorization: Bearer <token> (required)
  • Content-Type: application/json (required)
  • Idempotency-Key: <uuid> (recommended)

Request Body:

{
"content": "Updated content",
"type": "post",
"isLiked": true,
"isDisliked": false,
"isSaved": true,
"scheduledOn": "2024-12-31T12:00:00.000Z",
"image": "data:image/png;base64,..."
}

Response:

{
"id": "post_xxx",
"content": "Updated content",
"updatedAt": "2024-01-01T00:00:00.000Z"
}

Status Codes:

  • 200 - Success
  • 400 - Bad request (no valid fields to update or validation error)
  • 401 - Unauthorized
  • 403 - Forbidden (not post owner)
  • 404 - Post not found
  • 409 - Conflict (idempotency key already used)
  • 413 - Payload too large (image size limit exceeded)
  • 429 - Rate limit exceeded
  • 500 - Internal server error

Soft delete a post.

Endpoint: DELETE /api/v1/post/[id]

Headers:

  • Authorization: Bearer <token> (required)
  • Idempotency-Key: <uuid> (recommended)

Response:

{
"success": true,
"deletedAt": "2024-01-01T00:00:00.000Z"
}

Status Codes:

  • 200 - Success
  • 401 - Unauthorized
  • 403 - Forbidden (not post owner)
  • 404 - Post not found
  • 409 - Conflict (idempotency key already used)
  • 429 - Rate limit exceeded
  • 500 - Internal server error

Get all personas for the authenticated user.

Endpoint: GET /api/v1/persona

Response:

[
{
"id": "persona_xxx",
"name": "Tech Professional",
"description": "A tech-savvy professional persona",
"targetAudience": "Tech professionals",
"profession": "Software Engineer",
"relevantKeywords": "AI, ML, Tech",
"opinions": "Innovation-focused",
"vocabulary": "Technical terms",
"avoidWords": "Jargon",
"endGoal": "Engage tech community",
"customHashtag": "#TechLife",
"fixCta": "Call to action",
"createdAt": "2024-01-01T00:00:00.000Z",
"updatedAt": "2024-01-01T00:00:00.000Z"
}
]

Status Codes:

  • 200 - Success
  • 401 - Unauthorized
  • 429 - Rate limit exceeded
  • 500 - Internal server error

Create a new persona manually.

Endpoint: POST /api/v1/persona

Headers:

  • Authorization: Bearer <token> (required)
  • Content-Type: application/json (required)
  • Idempotency-Key: <uuid> (recommended)

Request Body:

{
"name": "Tech Professional",
"description": "A tech-savvy professional persona",
"defaultPrompt": "Default prompt",
"targetAudience": "Tech professionals",
"profession": "Software Engineer",
"relevantKeywords": "AI, ML, Tech",
"opinions": "Innovation-focused",
"vocabulary": "Technical terms",
"avoidWords": "Jargon",
"endGoal": "Engage tech community",
"customHashtag": "#TechLife",
"fixCta": "Call to action"
}

Response:

{
"id": "persona_xxx",
"name": "Tech Professional",
"createdAt": "2024-01-01T00:00:00.000Z"
}

Status Codes:

  • 201 - Created
  • 400 - Bad request (missing name or validation error)
  • 401 - Unauthorized
  • 402 - Payment required (quota exhausted)
  • 409 - Conflict (idempotency key already used)
  • 429 - Rate limit exceeded
  • 500 - Internal server error

Quota: Consumes personas quota (1 unit)

Rate Limits: 50 requests per hour per user


Generate a persona using AI based on a description.

Endpoint: POST /api/v1/persona/generate

Headers:

  • Authorization: Bearer <token> (required)
  • Content-Type: application/json (required)
  • Idempotency-Key: <uuid> (recommended)

Request Body:

{
"prompt": "Create a persona for a healthcare professional who focuses on patient care and medical innovation"
}

Response:

{
"id": "persona_xxx",
"name": "Healthcare Professional",
"description": "A healthcare professional focused on patient care...",
"targetAudience": "Healthcare professionals and patients",
"profession": "Medical Professional",
"relevantKeywords": "Healthcare, Medicine, Patient Care",
"opinions": "Patient-first approach",
"vocabulary": "Medical terminology",
"avoidWords": "Overly technical jargon",
"endGoal": "Educate and engage healthcare community",
"createdAt": "2024-01-01T00:00:00.000Z"
}

Status Codes:

  • 201 - Created
  • 400 - Bad request (invalid prompt or validation error)
  • 401 - Unauthorized
  • 402 - Payment required (quota exhausted)
  • 409 - Conflict (idempotency key already used)
  • 429 - Rate limit exceeded
  • 500 - Internal server error

Quota: Consumes personas quota (1 unit)

Rate Limits: 20 requests per hour per user


Retrieve a specific persona by ID.

Endpoint: GET /api/v1/persona/[id]

Response:

{
"id": "persona_xxx",
"name": "Tech Professional",
"description": "...",
"createdAt": "2024-01-01T00:00:00.000Z"
}

Status Codes:

  • 200 - Success
  • 401 - Unauthorized
  • 403 - Forbidden (not persona owner)
  • 404 - Persona not found
  • 429 - Rate limit exceeded
  • 500 - Internal server error

Update an existing persona.

Endpoint: PUT /api/v1/persona/[id]

Headers:

  • Authorization: Bearer <token> (required)
  • Content-Type: application/json (required)
  • Idempotency-Key: <uuid> (recommended)

Request Body:

{
"name": "Updated Name",
"description": "Updated description",
"targetAudience": "Updated audience"
}

Response:

{
"id": "persona_xxx",
"name": "Updated Name",
"updatedAt": "2024-01-01T00:00:00.000Z"
}

Status Codes:

  • 200 - Success
  • 400 - Bad request (validation error)
  • 401 - Unauthorized
  • 403 - Forbidden (not persona owner)
  • 404 - Persona not found
  • 409 - Conflict (idempotency key already used)
  • 429 - Rate limit exceeded
  • 500 - Internal server error

Soft delete a persona.

Endpoint: DELETE /api/v1/persona/[id]

Headers:

  • Authorization: Bearer <token> (required)
  • Idempotency-Key: <uuid> (recommended)

Response:

{
"success": true,
"deletedAt": "2024-01-01T00:00:00.000Z"
}

Status Codes:

  • 200 - Success
  • 401 - Unauthorized
  • 403 - Forbidden (not persona owner)
  • 404 - Persona not found
  • 409 - Conflict (idempotency key already used)
  • 429 - Rate limit exceeded
  • 500 - Internal server error

Note: Deletion decreases the persona usage count by 1.


Get personas in a simplified format for dropdowns.

Endpoint: GET /api/v1/persona/dropdown

Response:

[
{
"id": "persona_xxx",
"name": "Tech Professional"
}
]

Status Codes:

  • 200 - Success
  • 401 - Unauthorized
  • 429 - Rate limit exceeded
  • 500 - Internal server error

Generate a single comment for a LinkedIn post.

Endpoint: POST /api/v1/comments/generate

Headers:

  • Authorization: Bearer <token> (required)
  • Content-Type: application/json (required)
  • Idempotency-Key: <uuid> (recommended)

Request Body:

{
"postContent": "Original LinkedIn post content...",
"tone": "friendly",
"length": "short",
"platform": "linkedin",
"userComment": "Custom instruction or draft",
"authorName": "Jane Smith",
"authorUrl": "https://linkedin.com/in/janesmith",
"authorBio": "Product Manager at Tech Solutions Inc."
}

Response (LinkedIn):

{
"comment": {
"quote": "Great insights! This really resonates with my experience...",
"profileUrl": "https://linkedin.com/in/janesmith"
},
"message": "Comment generated and saved",
"generationTime": 2.5,
"platform": "linkedin"
}

Response (X/Twitter):

{
"comment": "Great insights! This really resonates...",
"message": "Reply generated",
"generationTime": 2.3,
"platform": "x"
}

Status Codes:

  • 200 - Success
  • 400 - Bad request (missing postContent, invalid platform, or validation error)
  • 401 - Unauthorized
  • 402 - Payment required (quota exhausted)
  • 409 - Conflict (idempotency key already used)
  • 429 - Rate limit exceeded
  • 500 - Internal server error

Quota: Consumes comment_generation quota (1 unit)

Rate Limits: 200 requests per hour per user


Generate 5 diverse comment variations in a single request.

Endpoint: POST /api/v1/comments/beast-mode

Headers:

  • Authorization: Bearer <token> (required)
  • Content-Type: application/json (required)
  • Idempotency-Key: <uuid> (recommended)

Request Body:

{
"postContent": "Original LinkedIn post content...",
"platform": "linkedin",
"userComment": "Custom instruction",
"authorName": "Jane Smith",
"authorBio": "Product Manager"
}

Response:

{
"comments": [
{
"quote": "Launch day! 🎉",
"type": "short"
},
{
"quote": "6 months well spent! Can't wait to try it out.",
"type": "short"
},
{
"quote": "That feeling when you finally ship... both exciting and nerve-wracking! Congrats on the launch.",
"type": "medium"
},
{
"quote": "Been there with product launches. The real work starts now with customer feedback!",
"type": "short"
},
{
"quote": "Love how you focused on customer pain points rather than just features. What was the biggest challenge during development?",
"type": "medium"
}
],
"message": "Beast mode comments generated",
"generationTime": 3.25,
"platform": "linkedin"
}

Status Codes:

  • 200 - Success
  • 400 - Bad request (missing postContent, invalid platform, or validation error)
  • 401 - Unauthorized
  • 402 - Payment required (quota exhausted, requires 5 units)
  • 409 - Conflict (idempotency key already used)
  • 429 - Rate limit exceeded
  • 500 - Internal server error

Quota: Consumes comment_generation quota (5 units)

Rate Limits: 40 requests per hour per user


Get all comments with pagination and search.

Endpoint: GET /api/v1/comments

Query Parameters:

  • page (number, default: 1, min: 1) - Page number
  • limit (number, default: 10, min: 1, max: 100) - Items per page
  • search (string, optional, max: 100 chars) - Search by first or last name

Response:

{
"comments": [
{
"id": "comment_xxx",
"firstName": "John",
"lastName": "Doe",
"title": "Software Engineer",
"quote": "Great post!",
"date": "2024-01-01T00:00:00.000Z"
}
],
"pagination": {
"total": 50,
"page": 1,
"limit": 10,
"totalPages": 5
}
}

Status Codes:

  • 200 - Success
  • 400 - Bad request (invalid query parameters)
  • 401 - Unauthorized
  • 429 - Rate limit exceeded
  • 500 - Internal server error

Rate Limits: 1000 requests per hour per user


Create a comment manually.

Endpoint: POST /api/v1/comments

Headers:

  • Authorization: Bearer <token> (required)
  • Content-Type: application/json (required)
  • Idempotency-Key: <uuid> (recommended)

Request Body:

{
"firstName": "John",
"lastName": "Doe",
"title": "Software Engineer",
"quote": "Great post!",
"image": ""
}

Response:

{
"id": "comment_xxx",
"firstName": "John",
"lastName": "Doe",
"quote": "Great post!",
"createdAt": "2024-01-01T00:00:00.000Z"
}

Status Codes:

  • 201 - Created
  • 400 - Bad request (missing required fields or validation error)
  • 401 - Unauthorized
  • 409 - Conflict (idempotency key already used)
  • 429 - Rate limit exceeded
  • 500 - Internal server error

Delete a comment.

Endpoint: DELETE /api/v1/comments/[id]

Headers:

  • Authorization: Bearer <token> (required)
  • Idempotency-Key: <uuid> (recommended)

Response:

{
"success": true,
"deletedAt": "2024-01-01T00:00:00.000Z"
}

Status Codes:

  • 200 - Success
  • 401 - Unauthorized
  • 403 - Forbidden (not comment owner)
  • 404 - Comment not found
  • 409 - Conflict (idempotency key already used)
  • 429 - Rate limit exceeded
  • 500 - Internal server error

Generate content ideas using AI based on a prompt.

Endpoint: POST /api/v1/idea

Headers:

  • Authorization: Bearer <token> (required)
  • Content-Type: application/json (required)
  • Idempotency-Key: <uuid> (recommended)

Request Body:

{
"prompt": "Ideas for tech industry content",
"threadId": "thread_xxx"
}

Response:

{
"thread": {
"id": "thread_xxx",
"title": "Ideas for tech industry content",
"type": "idea",
"createdAt": "2024-01-01T00:00:00.000Z"
},
"promptMessage": {
"id": "message_xxx",
"content": "Ideas for tech industry content",
"createdAt": "2024-01-01T00:00:00.000Z"
},
"responseMessage": {
"id": "message_xxx",
"content": "Generated ideas for: Ideas for tech industry content",
"contentIdeas": [
{
"id": "idea_xxx",
"title": "The Future of AI in Healthcare",
"content": "Detailed content description...",
"keyTakeaways": ["Takeaway 1", "Takeaway 2"],
"controversialAngle": "Controversial perspective..."
}
],
"createdAt": "2024-01-01T00:00:00.000Z"
}
}

Status Codes:

  • 200 - Success
  • 400 - Bad request (missing prompt or validation error)
  • 401 - Unauthorized
  • 402 - Payment required (quota exhausted)
  • 404 - Thread not found (if threadId provided)
  • 409 - Conflict (idempotency key already used)
  • 429 - Rate limit exceeded
  • 500 - Internal server error

Quota: Consumes idea_generation quota (1 unit per idea generated)

Rate Limits: 50 requests per hour per user


Retrieve content ideas by thread or message.

Endpoint: GET /api/v1/idea

Query Parameters:

  • threadId (string, optional) - Get all ideas for a thread
  • messageId (string, optional) - Get ideas for a specific message

Response:

[
{
"id": "idea_xxx",
"title": "The Future of AI in Healthcare",
"content": "Detailed content description...",
"keyTakeaways": ["Takeaway 1", "Takeaway 2"],
"controversialAngle": "Controversial perspective...",
"createdAt": "2024-01-01T00:00:00.000Z"
}
]

Status Codes:

  • 200 - Success
  • 400 - Bad request (invalid query parameters)
  • 401 - Unauthorized
  • 429 - Rate limit exceeded
  • 500 - Internal server error

Retrieve a specific content idea by ID.

Endpoint: GET /api/v1/idea/[id]

Response:

{
"id": "idea_xxx",
"title": "The Future of AI in Healthcare",
"content": "Detailed content description...",
"keyTakeaways": ["Takeaway 1", "Takeaway 2"],
"controversialAngle": "Controversial perspective..."
}

Status Codes:

  • 200 - Success
  • 401 - Unauthorized
  • 403 - Forbidden (not idea owner)
  • 404 - Idea not found
  • 429 - Rate limit exceeded
  • 500 - Internal server error

Generate dynamic feed posts based on user context (ideas, personas, liked/saved posts).

Endpoint: GET /api/v1/feed

Query Parameters:

  • limit (number, default: 5, min: 1, max: 5) - Number of posts to generate
  • cursor (string, optional) - Pagination cursor

Response:

{
"posts": [
{
"id": "post_xxx",
"content": {
"text": "Generated feed post content..."
},
"timestamp": "2024-01-01T00:00:00.000Z",
"isLiked": false,
"isDisliked": false,
"isSaved": false,
"metadata": {
"sourceType": "idea",
"sourceId": "idea_xxx",
"contentLength": "medium",
"generatedWithWebSearch": false
}
}
],
"nextCursor": "post_xxx",
"hasMore": true
}

Status Codes:

  • 200 - Success
  • 400 - Bad request (invalid query parameters)
  • 401 - Unauthorized
  • 402 - Payment required (quota exhausted)
  • 429 - Rate limit exceeded
  • 500 - Internal server error

Quota: Consumes post_generation quota (1 unit per post generated)

Rate Limits: 100 requests per hour per user

Note: The API uses user’s saved ideas, personas, liked posts, and saved posts as context for generation. May use web search (30% probability) for more relevant content.


Retrieve feed generation history.

Endpoint: GET /api/v1/feed/history

Query Parameters:

  • page (number, default: 1) - Page number
  • limit (number, default: 10, max: 100) - Items per page

Response:

[
{
"id": "post_xxx",
"content": "Feed post content",
"createdAt": "2024-01-01T00:00:00.000Z",
"source": "feed"
}
]

Status Codes:

  • 200 - Success
  • 400 - Bad request (invalid query parameters)
  • 401 - Unauthorized
  • 429 - Rate limit exceeded
  • 500 - Internal server error

Generate an image for a LinkedIn post using AI.

Endpoint: POST /api/v1/generate-image-replicate

Headers:

  • Authorization: Bearer <token> (required)
  • Content-Type: application/json (required)
  • Idempotency-Key: <uuid> (recommended)

Request Body:

{
"postContent": "LinkedIn post content...",
"postId": "post_xxx",
"customPrompt": "A professional image of..."
}

Response:

{
"success": true,
"imageUrl": "https://...",
"imageKey": "post/xxx"
}

Status Codes:

  • 200 - Success
  • 400 - Bad request (missing postContent or validation error)
  • 401 - Unauthorized
  • 402 - Payment required (quota exhausted)
  • 409 - Conflict (idempotency key already used)
  • 429 - Rate limit exceeded
  • 500 - Internal server error

Quota: Consumes image_creation quota (1 unit)

Rate Limits: 30 requests per hour per user

Note: If postId is provided, the image is automatically attached to the post. The API generates an image prompt from post content if customPrompt is not provided.


Download an image by key.

Endpoint: GET /api/v1/download-image

Query Parameters:

  • key (string, required) - Image key

Response: Image file (binary) with appropriate Content-Type header

Status Codes:

  • 200 - Success
  • 400 - Bad request (missing key)
  • 401 - Unauthorized
  • 403 - Forbidden (not image owner)
  • 404 - Image not found
  • 429 - Rate limit exceeded
  • 500 - Internal server error

Rate Limits: 1000 requests per hour per user


Refresh a signed image URL.

Endpoint: POST /api/v1/refresh-image-url

Headers:

  • Authorization: Bearer <token> (required)
  • Content-Type: application/json (required)

Request Body:

{
"imageKey": "post/xxx"
}

Response:

{
"imageUrl": "https://...",
"expiresAt": "2024-01-01T01:00:00.000Z"
}

Status Codes:

  • 200 - Success
  • 400 - Bad request (missing imageKey or validation error)
  • 401 - Unauthorized
  • 403 - Forbidden (not image owner)
  • 404 - Image not found
  • 429 - Rate limit exceeded
  • 500 - Internal server error

Get the current user’s subscription.

Endpoint: GET /api/v1/subscriptions

Response:

{
"id": "sub_xxx",
"userId": "user_xxx",
"planId": "plan_xxx",
"status": "active",
"currentPeriodStart": "2024-01-01T00:00:00.000Z",
"currentPeriodEnd": "2024-12-31T23:59:59.000Z",
"cancelAtPeriodEnd": false
}

Status Codes:

  • 200 - Success (returns null if no subscription)
  • 401 - Unauthorized
  • 429 - Rate limit exceeded
  • 500 - Internal server error

Cancel the current user’s subscription.

Endpoint: POST /api/v1/subscriptions/cancel

Headers:

  • Authorization: Bearer <token> (required)
  • Idempotency-Key: <uuid> (recommended)

Response:

{
"success": true,
"message": "Subscription will be cancelled at the end of the current period",
"cancelAt": "2024-12-31T23:59:59.000Z"
}

Status Codes:

  • 200 - Success
  • 401 - Unauthorized
  • 404 - No active subscription
  • 409 - Conflict (idempotency key already used)
  • 429 - Rate limit exceeded
  • 500 - Internal server error

Renew a free subscription.

Endpoint: POST /api/v1/subscriptions/renew-free

Headers:

  • Authorization: Bearer <token> (required)
  • Idempotency-Key: <uuid> (recommended)

Response:

{
"success": true,
"subscription": {
"id": "sub_xxx",
"status": "active",
"currentPeriodEnd": "2024-12-31T23:59:59.000Z"
}
}

Status Codes:

  • 200 - Success
  • 400 - Bad request (not eligible for free renewal)
  • 401 - Unauthorized
  • 409 - Conflict (idempotency key already used)
  • 429 - Rate limit exceeded
  • 500 - Internal server error

Get all active subscription plans.

Endpoint: GET /api/v1/plans

Response:

[
{
"id": "plan_xxx",
"name": "Pro Plan",
"description": "Professional features",
"price": 29.99,
"currency": "USD",
"interval": "month",
"features": ["Feature 1", "Feature 2"],
"postGenerationLimit": 100,
"ideaGenerationLimit": 50,
"personaLimit": 10,
"imageCreationLimit": 20,
"isActive": true
}
]

Status Codes:

  • 200 - Success
  • 401 - Unauthorized
  • 429 - Rate limit exceeded
  • 500 - Internal server error

Get a specific plan by ID.

Endpoint: GET /api/v1/plans/[id]

Response:

{
"id": "plan_xxx",
"name": "Pro Plan",
"price": 29.99,
"features": ["Feature 1", "Feature 2"]
}

Status Codes:

  • 200 - Success
  • 404 - Plan not found
  • 429 - Rate limit exceeded
  • 500 - Internal server error

Create a new subscription plan.

Endpoint: POST /api/v1/plans

Headers:

  • Authorization: Bearer <token> (required, admin role)
  • Content-Type: application/json (required)
  • Idempotency-Key: <uuid> (recommended)

Request Body:

{
"name": "Pro Plan",
"description": "Professional features",
"price": 29.99,
"currency": "USD",
"interval": "month",
"features": ["Feature 1", "Feature 2"],
"postGenerationLimit": 100,
"ideaGenerationLimit": 50,
"personaLimit": 10,
"imageCreationLimit": 20
}

Response:

{
"id": "plan_xxx",
"name": "Pro Plan",
"createdAt": "2024-01-01T00:00:00.000Z"
}

Status Codes:

  • 201 - Created
  • 400 - Bad request (missing required fields or validation error)
  • 401 - Unauthorized
  • 403 - Forbidden (admin role required)
  • 409 - Conflict (idempotency key already used)
  • 429 - Rate limit exceeded
  • 500 - Internal server error

Get all usage quotas for the current user.

Endpoint: GET /api/v1/usage/quotas

Response:

{
"post_generation": {
"limit": 100,
"used": 45,
"remaining": 55,
"resetAt": "2024-02-01T00:00:00.000Z"
},
"idea_generation": {
"limit": 50,
"used": 20,
"remaining": 30,
"resetAt": "2024-02-01T00:00:00.000Z"
},
"personas": {
"limit": 10,
"used": 3,
"remaining": 7,
"resetAt": null
},
"comment_generation": {
"limit": 200,
"used": 150,
"remaining": 50,
"resetAt": "2024-02-01T00:00:00.000Z"
},
"image_creation": {
"limit": 20,
"used": 5,
"remaining": 15,
"resetAt": "2024-02-01T00:00:00.000Z"
}
}

Status Codes:

  • 200 - Success
  • 401 - Unauthorized
  • 429 - Rate limit exceeded
  • 500 - Internal server error

Check if a specific quota is available.

Endpoint: GET /api/v1/quotas/check

Query Parameters:

  • type (string, required) - Quota type: post_generation, idea_generation, personas, comment_generation, image_creation

Response (Available):

{
"available": true,
"quotaType": "post_generation",
"remaining": 55,
"limit": 100
}

Response (Exhausted):

{
"available": false,
"error": "Quota exhausted",
"quotaType": "post_generation",
"upgradeRequired": true,
"remaining": 0,
"limit": 100,
"resetAt": "2024-02-01T00:00:00.000Z"
}

Status Codes:

  • 200 - Available
  • 400 - Bad request (missing type or invalid type)
  • 401 - Unauthorized
  • 402 - Payment required (quota exhausted)
  • 429 - Rate limit exceeded
  • 500 - Internal server error

Get all saved posts and content ideas.

Endpoint: GET /api/v1/saved

Query Parameters:

  • type (string, optional) - Filter by type: "post" | "idea" (null for all)
  • cursor (string, optional) - Pagination cursor
  • limit (number, default: 10, min: 1, max: 100) - Items per page

Response:

{
"posts": [
{
"id": "post_xxx",
"content": {
"text": "Post content",
"image": "https://...",
"imageKey": "post/xxx"
},
"timestamp": "2024-01-01T00:00:00.000Z",
"isSaved": true,
"isLiked": false,
"isDisliked": false,
"isPostedOnLinkedin": false,
"linkedinUrl": null,
"linkedinPostedAt": null,
"scheduledOn": null
}
],
"ideas": [
{
"id": "idea_xxx",
"title": "Content Idea",
"content": "Idea content...",
"keyTakeaways": ["Takeaway 1"],
"controversialAngle": "Angle..."
}
],
"nextCursor": "post_xxx",
"hasMore": true
}

Status Codes:

  • 200 - Success
  • 400 - Bad request (invalid query parameters)
  • 401 - Unauthorized
  • 404 - User not found
  • 429 - Rate limit exceeded
  • 500 - Internal server error

Share a post directly to LinkedIn.

Endpoint: POST /api/v1/post/share-linkedin

Headers:

  • Authorization: Bearer <token> (required)
  • Content-Type: application/json (required)
  • Idempotency-Key: <uuid> (recommended)

Request Body:

{
"postId": "post_xxx"
}

Response:

{
"success": true,
"shareUrl": "https://www.linkedin.com/feed/update/xxx",
"sharedAt": "2024-01-01T00:00:00.000Z"
}

Status Codes:

  • 200 - Success
  • 400 - Bad request (missing postId or validation error)
  • 401 - Unauthorized
  • 403 - Forbidden (missing LinkedIn permission or not post owner)
  • 404 - Post not found
  • 409 - Conflict (idempotency key already used)
  • 429 - Rate limit exceeded
  • 500 - Internal server error

Note: Requires LinkedIn OAuth with w_member_social scope.

Rate Limits: 50 requests per hour per user


Publish scheduled posts (internal/cron endpoint).

Endpoint: POST /api/v1/post/publish

Headers:

  • Authorization: Bearer <api-key> (required, internal API key)
  • Content-Type: application/json (required)
  • X-API-Key: <key> (required, alternative to Authorization header)

Request Body:

{
"apiKey": "xxx"
}

Response:

{
"success": true,
"totalProcessed": 5,
"successCount": 4,
"failureCount": 1,
"results": [
{
"postId": "post_xxx",
"success": true,
"shareUrl": "https://www.linkedin.com/feed/update/xxx"
}
],
"processedAt": "2024-01-01T00:00:00.000Z"
}

Status Codes:

  • 200 - Success
  • 400 - Bad request (invalid format)
  • 401 - Unauthorized (invalid API key)
  • 429 - Rate limit exceeded
  • 500 - Internal server error

Note: This endpoint is designed for cron jobs or external services. It processes posts scheduled within the next 2 minutes or past scheduled posts that haven’t been posted yet.


Handle Clerk user synchronization events.

Endpoint: POST /api/v1/webhook/clerk

Headers:

  • svix-id: <id> (required, webhook ID)
  • svix-timestamp: <timestamp> (required, webhook timestamp)
  • svix-signature: <signature> (required, webhook signature)

Request: Clerk webhook payload (verified via signature)

Response:

{
"success": true,
"processedAt": "2024-01-01T00:00:00.000Z"
}

Status Codes:

  • 200 - Success
  • 400 - Bad request (invalid payload)
  • 401 - Unauthorized (invalid webhook signature)
  • 500 - Internal server error

Note: Used to sync user data (firstname, email, lastname, photo) from Clerk to the database. All webhook requests are verified using signature validation.


Handle payment and subscription events from DodoPayments.

Endpoint: POST /api/v1/webhook/dodopayments

Headers:

  • X-Webhook-Signature: <signature> (required, webhook signature)
  • X-Webhook-Timestamp: <timestamp> (required, webhook timestamp)

Request: DodoPayments webhook payload (verified via signature)

Response:

{
"success": true,
"processedAt": "2024-01-01T00:00:00.000Z"
}

Status Codes:

  • 200 - Success
  • 400 - Bad request (invalid payload)
  • 401 - Unauthorized (invalid webhook signature)
  • 500 - Internal server error

Note: Handles subscription creation, updates, cancellations, and payment events. All webhook requests are verified using signature validation.


All error responses follow a consistent format:

{
"error": {
"code": "ERROR_CODE",
"message": "Human-readable error message",
"details": "Additional error details",
"requestId": "req_xxx",
"timestamp": "2024-01-01T00:00:00.000Z"
},
"quota": {
"type": "post_generation",
"limit": 100,
"used": 100,
"remaining": 0,
"resetAt": "2024-02-01T00:00:00.000Z"
}
}
  • 200 - Success
  • 201 - Created
  • 400 - Bad Request (invalid parameters, validation errors)
  • 401 - Unauthorized (authentication required or invalid token)
  • 402 - Payment Required (quota exhausted)
  • 403 - Forbidden (insufficient permissions)
  • 404 - Not Found
  • 409 - Conflict (idempotency key already used, resource conflict)
  • 413 - Payload Too Large (request body exceeds size limit)
  • 429 - Too Many Requests (rate limit exceeded)
  • 500 - Internal Server Error
  • 503 - Service Unavailable (temporary service outage)

All API responses include rate limiting headers:

  • X-RateLimit-Limit: Maximum requests per period
  • X-RateLimit-Remaining: Remaining requests in current period
  • X-RateLimit-Reset: Unix timestamp when the rate limit resets

When rate limit is exceeded, the API returns 429 Too Many Requests with a Retry-After header indicating seconds to wait before retrying.

Rate limits are applied per user and vary by endpoint:

  • Read operations: 1000 requests/hour
  • Write operations: 500 requests/hour
  • AI generation: 20-200 requests/hour (varies by endpoint)
  • Webhooks: No rate limiting (signature verification required)

Used for endpoints that return large datasets with consistent ordering:

  • Use cursor query parameter for pagination
  • Response includes nextCursor and hasMore fields
  • Cursor is an opaque string that should be passed as-is
  • When hasMore is false, no more results are available

Used for endpoints that support flexible sorting:

  • Use page and limit query parameters
  • Response includes totalCount, totalPages, and hasMore fields
  • page starts at 1
  • limit has a maximum value (typically 100)

All request bodies and query parameters are validated according to OpenAPI schemas. Validation errors return 400 Bad Request with detailed error messages indicating which fields failed validation.

Validation Rules:

  • Required fields must be present
  • String fields have maximum length limits
  • Numeric fields have min/max value constraints
  • Enum fields must match allowed values
  • Date fields must be valid ISO 8601 format
  • Email fields must be valid email format
  • URL fields must be valid URLs

For state-changing operations (POST, PUT, DELETE), include an Idempotency-Key header with a unique UUID. This ensures that if a request is retried due to network issues, the operation is not performed multiple times.

Idempotency Behavior:

  • First request with a key: Operation is performed, response is cached
  • Subsequent requests with the same key: Returns cached response with 409 Conflict status
  • Idempotency keys are valid for 24 hours
  • Keys are scoped per user and endpoint

The API supports Cross-Origin Resource Sharing (CORS) for authorized domains. CORS headers are automatically included in responses:

  • Access-Control-Allow-Origin: Allowed origin
  • Access-Control-Allow-Methods: Allowed HTTP methods
  • Access-Control-Allow-Headers: Allowed request headers
  • Access-Control-Max-Age: Cache duration for preflight requests

A complete OpenAPI 3.0 specification is available at /api/v1/openapi.json and /api/v1/openapi.yaml. This specification includes:

  • All endpoints with request/response schemas
  • Authentication requirements
  • Rate limiting information
  • Error response formats
  • Example requests and responses

Interactive API documentation is available at /api/v1/docs (Swagger UI).


All webhook endpoints require signature verification to ensure requests are authentic:

  1. Clerk Webhooks: Use Svix signature verification

    • Verify svix-signature header
    • Check svix-timestamp for replay attacks
    • Validate payload against signature
  2. DodoPayments Webhooks: Use HMAC signature verification

    • Verify X-Webhook-Signature header
    • Check X-Webhook-Timestamp for replay attacks
    • Validate payload against secret key

Webhook signatures must be verified within 5 minutes of the timestamp to prevent replay attacks.


All API requests are logged with:

  • Request ID (from X-Request-ID header or generated)
  • User ID
  • Endpoint and method
  • Request timestamp
  • Response status code
  • Processing time
  • Error details (if applicable)

Performance metrics are tracked including:

  • Request latency (p50, p95, p99)
  • Error rates by endpoint
  • Quota usage patterns
  • Rate limit violations

  • Timestamps: All timestamps are in ISO 8601 format (UTC): 2024-01-01T00:00:00.000Z
  • Image Uploads: Accept base64-encoded data URIs with format: data:image/<type>;base64,<data>
  • Maximum Image Size: 10MB per image
  • Maximum Request Body Size: 5MB
  • Content-Type: All requests must include Content-Type: application/json header for JSON payloads

All delete operations perform soft deletes:

  • Records are marked as deleted with isDeleted: true flag
  • Deleted records are not returned in list queries
  • Deleted records can be restored within 30 days
  • Permanent deletion occurs after 30 days

System personas are pre-configured personas available to all users:

  • System personas are not stored in user databases
  • System personas can be used in post generation
  • System personas have example posts and knowledge stacks
  • System personas are identified by special IDs

Post and idea generation support thread-based conversations:

  • Threads group related messages together
  • Threads maintain conversation context
  • Previous messages in a thread inform AI generation
  • Threads can be created automatically or explicitly
  • Thread history is limited to last 10 messages for context

API changes are documented and versioned. Breaking changes result in a new API version. Non-breaking changes are added to the current version.

Current Version: v1

Deprecation Policy:

  • Deprecated endpoints are marked in documentation
  • Deprecated endpoints remain functional for 6 months
  • Clients receive deprecation warnings in response headers
  • Breaking changes require a new API version