Quick Start

The Attention Factory API is rentals-and-actions first. You rent a real, verified social account for a whole month, then submit actions (posts, comments, likes, follows, branding changes) against it. This guide walks through your first rental in five steps.

1. Sign up and get your API key

Create your account on the dashboard, then generate an API key from the API Keys page. Every request below requires it in theAuthorization header.

2. Create a rental

Pick a platform and a duration (in whole months). Optionally supply filters, a branding preset, or an opt-in warming niche. The response is always 202 Accepted — the rental may be active immediately, orwarming while the account is prepared.

POST /v1/rentals
curl -X POST https://api.attentionfactory.com/v1/rentals \
  -H "Authorization: Bearer af_live_..." \
  -H "Content-Type: application/json" \
  -d '{
    "platform": "instagram",
    "months": 1,
    "auto_renew": true
  }'
Response
{
  "id": "4e1a5c98-1d23-4c9e-b3a7-0fdbc2a6e1b3",
  "anonymized_id": null,
  "platform": "instagram",
  "rental_start": null,
  "rental_end": null,
  "monthly_rate": "99.00",
  "credits_charged": "99.00",
  "auto_renew": true,
  "status": "warming",
  "expires_pending_at": "2026-05-02T12:00:00Z",
  "branding_preset": null,
  "created_at": "2026-04-18T12:00:00Z",
  "profile": null
}

3. Wait for activation

Poll the rental or register a webhook for rental.active. Once status flips toactive with a non-null anonymized_id, the account is yours for the rental window. On active rentals, the response includes a live profile object with the account's real username, display name, avatar, and follower counts.

GET /v1/rentals/{id}
curl https://api.attentionfactory.com/v1/rentals/4e1a5c98-1d23-4c9e-b3a7-0fdbc2a6e1b3 \
  -H "Authorization: Bearer af_live_..."
Response (active rental)
{
  "id": "4e1a5c98-1d23-4c9e-b3a7-0fdbc2a6e1b3",
  "anonymized_id": "7c2d9f5b-e48a-4c11-8b3f-91c2e4d8a6b7",
  "platform": "instagram",
  "rental_start": "2026-04-18T12:30:00Z",
  "rental_end": "2026-05-18T12:30:00Z",
  "monthly_rate": "99.00",
  "credits_charged": "99.00",
  "auto_renew": true,
  "status": "active",
  "expires_pending_at": null,
  "branding_preset": null,
  "created_at": "2026-04-18T12:00:00Z",
  "profile": {
    "username": "launchpad_labs",
    "display_name": "Launchpad",
    "avatar_url": "https://cdn.example.com/avatars/launchpad.jpg",
    "follower_count": 2340,
    "following_count": 180,
    "post_count": 47
  }
}

4. Submit an action

Actions are scoped to a rental + platform. The body carries atype (e.g.post.image) and action-specificparams. The rental must beactive.

POST /v1/rentals/{rental_id}/{platform}/actions
curl -X POST https://api.attentionfactory.com/v1/rentals/$RENTAL_ID/instagram/actions \
  -H "Authorization: Bearer af_live_..." \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: launch-post-001" \
  -d '{
    "type": "post.image",
    "params": {
      "caption": "Our new launch is live.",
      "media_url": "https://cdn.example.com/launch.jpg"
    }
  }'

5. Track completion via webhook

Register a webhook once and receive action status transitions (action.assigned, action.submitted, action.verified, etc.) in near-real-time.

POST /v1/webhooks
curl -X POST https://api.attentionfactory.com/v1/webhooks \
  -H "Authorization: Bearer af_live_..." \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://acme.com/webhooks/af",
    "events": ["rental.active", "action.verified", "action.failed"]
  }'

Authentication

All API requests require an API key passed in the Authorization header.

Authorization: Bearer af_live_...

API Key Format

API keys have the form af_live_<random> and are shown in full only once, at creation. Keys are generated and revoked from the API Keys page in the dashboard.

Platforms

Discovery endpoints for supported platforms, the actions they expose, and the niches available for opt-in warming. Call these before creating a rental to validate input client-side.

Endpoints

GET
/v1/platforms

List supported platforms

GET
/v1/platforms/{platform}/capabilities

Action types and per-rental rate caps for a platform

GET
/v1/platforms/{platform}/niches

Valid warming_niche values for opt-in warming

List Platforms

GET /v1/platforms
curl https://api.attentionfactory.com/v1/platforms \
  -H "Authorization: Bearer af_live_..."
Response
{
  "platforms": ["instagram", "tiktok", "reddit", "facebook", "linkedin"]
}

Platform Capabilities

Returns the action types available on a platform and the rate caps applied per rental. Use this to know which type values are valid inPOST .../actions.

GET /v1/platforms/{platform}/capabilities
curl https://api.attentionfactory.com/v1/platforms/instagram/capabilities \
  -H "Authorization: Bearer af_live_..."

Niche Catalog

Opt-in warming pre-primes a rented account for a specific audience over a 7-day pre-activation window. Use one of these values as warming_niche when creating a rental. Unknown values are rejected with a 400 before any credits are charged.

GET /v1/platforms/{platform}/niches
curl https://api.attentionfactory.com/v1/platforms/instagram/niches \
  -H "Authorization: Bearer af_live_..."
Response
{
  "platform": "instagram",
  "niches": ["fitness", "food", "tech-reviews", "travel"]
}

Rent an Account

Reserve a verified social account for a whole month. Pricing is monthly; actions incur no per-call charges. Every rental is charged upfront; if warming doesn't complete within 14 days the rental auto-refunds.

Endpoints

POST
/v1/rentals

Create a rental (returns 202)

GET
/v1/rentals

List your rentals (supports ?status=: warming · active · expired · refunded · cancelled · released)

GET
/v1/rentals/{id}

Get rental detail

PATCH
/v1/rentals/{id}

Update auto_renew (and other mutable fields)

DELETE
/v1/rentals/{id}

Release early or cancel a pending rental

POST
/v1/rentals/{id}/extend

Extend an active rental by N months

Create Rental

POST /v1/rentals
curl -X POST https://api.attentionfactory.com/v1/rentals \
  -H "Authorization: Bearer af_live_..." \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: my-rental-001" \
  -d '{
    "platform": "instagram",
    "months": 1,
    "auto_renew": true,
    "warming_niche": "tech-reviews",
    "branding": {
      "bio": "Builder. Coffee. Code.",
      "display_name": "Launchpad"
    }
  }'

Request Body

FieldTypeRequiredDescription
platformstringYesOne of: instagram, tiktok, reddit, facebook, linkedin
monthsintegerNo1–12. Defaults to 1
auto_renewboolNoAuto-renew when the rental window closes. Defaults to true
warming_nichestringNoOpt in to 7-day pre-warming on this niche. Must be in the platform's niche catalog
brandingobjectNoBranding preset applied automatically on activation

The branding object accepts any subset ofbio,display_name,avatar_url,banner_url, andusername. Each field is applied as a queued action the moment the rental activates.

Rental Status Values

StatusMeaning
warmingPre-activation. The account is being prepared and actions cannot be submitted yet. expires_pending_at indicates when the rental auto-refunds if warming doesn't complete
activeRental is live. Submit actions against the rental
expiredRental window closed naturally (auto-renew disabled or failed)
refundedCredits returned. Warming did not complete within the 14-day guarantee window
releasedCustomer released the rental early. No refund (month-committal)
cancelledCustomer cancelled while still pre-activation. Credits refunded

Extend / Renew

POST /v1/rentals/{id}/extend
curl -X POST https://api.attentionfactory.com/v1/rentals/$RENTAL_ID/extend \
  -H "Authorization: Bearer af_live_..." \
  -H "Content-Type: application/json" \
  -d '{"months": 2}'

Toggle Auto-Renew

PATCH /v1/rentals/{id} updates mutable rental fields. Today the only customer-controllable field is auto_renew; flip it any time before the rental window closes. Returns the updated rental.

PATCH /v1/rentals/{rental_id}
curl -X PATCH https://api.attentionfactory.com/v1/rentals/$RENTAL_ID \
  -H "Authorization: Bearer af_live_..." \
  -H "Content-Type: application/json" \
  -d '{"auto_renew": false}'

Brand an Account

Change your rented account's bio, display name, avatar, banner, and username in one request. The server fans out to one queued action per field you provide. Rate caps and review modes apply per sub-action.

Endpoint

POST
/v1/rentals/{id}/branding

Apply bio / display name / avatar / banner / username in one batch

Field support differs by platform — the sample below changes as you switch platforms. Fields not supported on the selected platform are simply omitted from the example.

Platform

POST /v1/rentals/{rental_id}/branding (instagram)
curl -X POST https://api.attentionfactory.com/v1/rentals/$RENTAL_ID/branding \
  -H "Authorization: Bearer af_live_..." \
  -H "Content-Type: application/json" \
  -d '{
  "bio": "Builder. Coffee. Code.",
  "display_name": "Launchpad",
  "avatar_url": "https://cdn.example.com/avatar.jpg",
  "username": "launchpad_labs",
  "pronouns": "she/her",
  "category": "Artist"
}'
Response (rental is active — mode: applied)
{
  "rental_id": "4e1a5c98-1d23-4c9e-b3a7-0fdbc2a6e1b3",
  "platform": "instagram",
  "mode": "applied",
  "results": [
    {"field": "bio", "action_type": "branding.bio", "action_id": "9ab72c01-...", "status": "queued"},
    {"field": "display_name", "action_type": "branding.display_name", "action_id": "f2c91e45-...", "status": "queued"}
  ],
  "preset": null
}

If the rental is still warming, the request returnsmode: "preset_stored" instead — the batch is saved and applied automatically when the rental activates. Re-posting while pending REPLACES the previously stored preset. At least one field must be non-null (empty bodies return 400).

Warm an Account

Enroll your rented account in a warming schedule — the app gradually builds authentic engagement over the duration you pick. You can pre-warm at rental creation via the warming_niche field, or enroll post-creation via this endpoint.

Endpoints

POST
/v1/rentals/{id}/warming

Enroll the rental in a warming schedule

DELETE
/v1/rentals/{id}/warming

Cancel a pending warming enrolment

Request Body

FieldTypeDefaultDescription
nichestringOptional, max 50 chars. See GET /v1/platforms/{platform}/niches for the platform's catalog. Pace and duration are managed server-side

Enroll

POST /v1/rentals/{rental_id}/warming
curl -X POST https://api.attentionfactory.com/v1/rentals/$RENTAL_ID/warming \
  -H "Authorization: Bearer af_live_..." \
  -H "Content-Type: application/json" \
  -d '{"niche": "tech-reviews"}'
Response
{
  "rental_id": "4e1a5c98-1d23-4c9e-b3a7-0fdbc2a6e1b3",
  "action_id": "9ab72c01-5d62-4f0b-a8cc-1d4a7b2fc309",
  "status": "queued"
}

Cancel

DELETE /v1/rentals/{rental_id}/warming
curl -X DELETE https://api.attentionfactory.com/v1/rentals/$RENTAL_ID/warming \
  -H "Authorization: Bearer af_live_..."

Cancels the most recent warming enrolment on the rental if it's still queued. 404 if there's no matching active enrolment.

Submit Actions

Actions are the unit of work performed on a rented account. They run asynchronously on real people's devices: the request returns 202 Accepted with a queued status, and the final state arrives via webhook or poll.

Endpoints

POST
/v1/rentals/{rental_id}/{platform}/actions

Submit a new action

GET
/v1/rentals/{rental_id}/actions

List actions on a rental (supports ?status=: queued · assigned · in_progress · submitted · verified · failed · cancelled)

GET
/v1/rentals/{rental_id}/actions/{action_id}

Get action detail

DELETE
/v1/rentals/{rental_id}/actions/{action_id}

Cancel a still-queued action

GET
/v1/rentals/{rental_id}/actions/{action_id}/analytics

Post-level analytics for post-type actions

Submit Action

The URL carries both the rental and platform; the body carries the actiontype and typedparams. Supply anIdempotency-Key header for retries — submitting twice with the same key returns the existing action instead of creating a duplicate. Availability varies by platform; pick a platform and an action below to see the exact request and response. Rate limits also vary per platform and action — call GET /v1/platforms/{platform}/capabilities for the definitive live list with caps.

Platform

Action

Publish a single image with optional caption. Supported on Instagram, Facebook, X, LinkedIn. All TikTok/IG composition options from the shared PostOptionsMixin apply: sound_name, location, link_url, allow_comments, allow_duet, allow_stitch, ai_generated, allow_visual_search, audience_control. LinkedIn composer options also accepted (ignored on other platforms): `visibility` (anyone / connections_only / group), `group_url` (required when visibility="group"; must be an https://linkedin.com/... URL), `comment_control` (anyone / connections_only / no_one), `brand_partner_url` (LinkedIn company URL — presence enables the Paid Partnership label). Include only the fields you want to set — omit the rest to keep the platform default.

POST /v1/rentals/{rental_id}/instagram/actions
curl -X POST https://api.attentionfactory.com/v1/rentals/$RENTAL_ID/instagram/actions \
  -H "Authorization: Bearer af_live_..." \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: my-action-001" \
  -d '{
  "type": "post.image",
  "params": {
    "media_url": "https://cdn.example.com/hero.jpg",
    "caption": "Launching today.",
    "location": "San Francisco, CA",
    "allow_comments": true,
    "audience_control": "everyone"
  }
}'
Response
{
  "id": "9ab72c01-5d62-4f0b-a8cc-1d4a7b2fc309",
  "rental_id": "4e1a5c98-1d23-4c9e-b3a7-0fdbc2a6e1b3",
  "platform": "instagram",
  "action_type": "post.image",
  "status": "queued",
  "created_at": "2026-04-19T12:05:00Z"
}

Action Status Values

StatusMeaning
queuedAccepted and awaiting a provider
assignedProvider picked up the action
in_progressProvider is working
submittedProvider submitted proof; awaiting verification
verifiedTerminal: action completed successfully
failedTerminal: action failed or was rejected on review
Reviews are automatic. You don't approve actions by hand. Status transitions arrive via your existing action.* webhooks andGET /v1/rentals/{id}/actions — wait forverified or failed.

Analytics

Read account-level and post-level metrics.

Endpoints

GET
/v1/rentals/{id}/analytics

Account-level analytics from the upstream provider (active rentals only)

GET
/v1/rentals/{id}/actions/{action_id}/analytics

Post-level analytics for a single post-type action

GET
/v1/rentals/{id}/capabilities

Action types available for this rental's platform

Account Analytics

GET /v1/rentals/{id}/analytics returns the latest account-level metrics (followers, following, post count, engagement) from the upstream provider. Only available while the rental isactive; other statuses return 400. Coverage varies per platform — TikTok typically returns every metric, Reddit returns few.

GET /v1/rentals/{rental_id}/analytics
curl https://api.attentionfactory.com/v1/rentals/$RENTAL_ID/analytics \
  -H "Authorization: Bearer af_live_..."

Post Analytics

Post-type actions expose upstream post-level analytics (views, likes, comments). Non-post action types return 400. Newly-verified posts may take a few seconds before analytics are populated — retry on 400 with "not yet available".

GET /v1/rentals/{rental_id}/actions/{action_id}/analytics
curl https://api.attentionfactory.com/v1/rentals/$RENTAL_ID/actions/$ACTION_ID/analytics \
  -H "Authorization: Bearer af_live_..."

Profile

GET /v1/rentals/{id} includes a live profile object on active rentals: username,display_name,avatar_url, and follower / following / post counts. Count fields report null if the platform doesn't supply them. The profile object itself is nullbefore activation, on terminal statuses, and on transient fetch failure — the rental itself never fails because the profile fetch did. Not returned on GET /v1/rentals (list); fetch each rental individually for profile data.

Capabilities

GET /v1/rentals/{id}/capabilities is a convenience for your UI — same shape as GET /v1/platforms/{platform}/capabilities, scoped to the rental's platform. Lets you check "what can I do with this rental right now" without mapping rental → platform yourself.

Billing

Track your credit balance and transaction history. Rentals are charged upfront at the monthly rate for each whole month requested.

Endpoints

GET
/v1/billing/balance

Get current credit balance

GET
/v1/billing/transactions

List transaction history

Balance

GET /v1/billing/balance
curl https://api.attentionfactory.com/v1/billing/balance \
  -H "Authorization: Bearer af_live_..."

Transactions

GET /v1/billing/transactions
curl https://api.attentionfactory.com/v1/billing/transactions \
  -H "Authorization: Bearer af_live_..."

Webhooks

Register HTTPS endpoints to receive real-time notifications when rentals change state and actions progress. Deliveries are signed with HMAC-SHA256.

Endpoints

POST
/v1/webhooks

Register a webhook endpoint

GET
/v1/webhooks

List registered webhooks

DELETE
/v1/webhooks/{id}

Remove a webhook

POST
/v1/webhooks/{id}/rotate-secret

Rotate the HMAC signing secret

GET
/v1/webhooks/{id}/deliveries

View delivery log for a webhook

Register Webhook

POST /v1/webhooks
curl -X POST https://api.attentionfactory.com/v1/webhooks \
  -H "Authorization: Bearer af_live_..." \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://acme.com/webhooks/af",
    "events": ["rental.active", "action.verified", "action.failed"]
  }'

The signing secret is generated by the server and returned once in the create response — you don't supply your own. Store it securely; it is encrypted at rest and cannot be retrieved again. If it leaks, rotate it viaPOST /v1/webhooks/{id}/rotate-secret.

Response (secret shown once)
{
  "id": "b7c1f0a2-9e54-4d3a-8c21-6f0a1b2c3d4e",
  "url": "https://acme.com/webhooks/af",
  "events": ["rental.active", "action.verified", "action.failed"],
  "is_active": true,
  "created_at": "2026-04-18T12:00:00Z",
  "secret": "9f8e7d6c5b4a39281706f5e4d3c2b1a0"
}

Event Types

Every delivery wraps the event data in an envelope containing the event name, a unique event_id, a timestamp (integer Unix seconds), and the event-specificdata object.

Rental events

EventWhen it fires
rental.activeRental transitions from warming to active and can accept actions
rental.cancelledCustomer cancelled a still-pending rental; credits refunded
rental.refundedPending window elapsed or pre-warming could not complete; credits refunded
rental.expiredRental window closed naturally (auto-renew off or failed)
rental.releasedCustomer released an active rental early; no refund

Action events

Emitted as action.{status} for each status transition. Subscribe only to the ones you need.

EventMeaning
action.assignedProvider picked it up (first action webhook)
action.in_progressProvider started work
action.submittedProof submitted by provider
action.verifiedTerminal: succeeded
action.failedTerminal: failed or rejected

queued is the status returned synchronously byPOST .../actions — it is not delivered as a webhook; the first action webhook isaction.assigned. Intermediate events are best-effort and may arrive out of order or be skipped (stale transitions are dropped server-side), so drive your logic off the terminalaction.verified / action.failed.

Example action.verified payload
{
  "event": "action.verified",
  "event_id": "c0ffee00-1d23-4c9e-b3a7-0fdbc2a6e1b3",
  "timestamp": 1745064600,
  "data": {
    "action_id": "9ab72c01-5d62-4f0b-a8cc-1d4a7b2fc309",
    "rental_id": "4e1a5c98-1d23-4c9e-b3a7-0fdbc2a6e1b3",
    "platform": "instagram",
    "action_type": "post.image",
    "status": "verified"
  }
}

Signature Verification

Each delivery includes an X-Webhook-Signature header of the form t=<unix>,v1=<hex>. The signature is an HMAC-SHA256, keyed with your webhook secret, over "<timestamp>." + rawBody — the raw received bytes, not a re-serialized object. Verify the signature and reject timestamps outside a ~5 minute window (replay protection) before trusting the payload.

Signature verification (Node.js)
const crypto = require('crypto');

// X-Webhook-Signature: t=<unix>,v1=<hex>
// rawBody MUST be the exact bytes you received (do not re-stringify the JSON).
function verifyWebhook(rawBody, signatureHeader, secret, toleranceSec = 300) {
  const parts = Object.fromEntries(
    signatureHeader.split(',').map((kv) => kv.trim().split('='))
  );
  const timestamp = parseInt(parts.t, 10);
  const provided = parts.v1 || '';
  if (!timestamp || !provided) return false;

  // Reject replays outside the freshness window.
  if (Math.abs(Math.floor(Date.now() / 1000) - timestamp) > toleranceSec) return false;

  const expected = crypto
    .createHmac('sha256', secret)
    .update(`${timestamp}.` + rawBody)
    .digest('hex');

  if (provided.length !== expected.length) return false;
  return crypto.timingSafeEqual(Buffer.from(provided), Buffer.from(expected));
}

A legacy X-Webhook-Signature-Legacy header (sha256=<hex>) is sent alongside during the transition window; new integrations should verify X-Webhook-Signature and ignore it.

Idempotent handlers recommended. Deliveries are retried on non-2xx responses, so the same event may arrive more than once. De-dupe on the envelope's event_id on your side.

Errors

The API uses standard HTTP status codes. All error responses include a detail field with a human-readable message.

Error Response Format

Error response
{
  "detail": "Insufficient credits. Need 99.00, have 12.00"
}

Status Codes

CodeMeaningCommon Cause
400Bad RequestMissing or invalid fields (e.g. unknown warming_niche)
401UnauthorizedMissing or invalid API key
402Payment RequiredInsufficient credit balance
403ForbiddenAction not allowed for your subscription tier
404Not FoundResource does not exist or not owned by your business
409ConflictInvalid state transition (e.g. extending an expired rental)
422Unprocessable EntityAction params failed schema validation
429Too Many RequestsPer-rental rate cap exceeded (see Retry-After header)
500Internal Server ErrorSomething went wrong on our end