ofapisv1
integration

Updated

Connect OnlyFans to Claude & ChatGPT

Every current assistant can call an HTTP API. ofapis is REST with a full OpenAPI 3.0 document, so the API side of this is already done — one base URL, https://api.ofapis.com/api/public/v1, and one Authorization: Bearer ofapis_sk_... header, with no request signing and no OAuth refresh for the assistant to manage.

What you are actually choosing between is three different places for the HTTP request to execute, three different owners of the credential, and three very different answers to "who says yes before a DM goes to a real person". That choice matters more than which model you use, so this page is about picking it.

The three wiring styles

Custom GPT Actions MCP client Your own code
Runs the request OpenAI's servers a process on your machine your server
Holds the token the GPT's action auth the bridge's env your secret store
Approval control per-action Confirm/Allow whatever the client offers anything you write
Setup cost minutes, no code one config file a day or two
Audit trail the GPT's call log the bridge's stdout your logs
Good for solo creators, exploring one operator, real work agencies, many accounts

Start at ChatGPT Custom GPT Actions for the first column and Claude Desktop or Claude Code for the second. The MCP reference covers how an OpenAPI document becomes a tool catalogue in either case.

Hosted assistant versus local client

The split that matters is not Claude versus GPT. It is whether the request leaves from a vendor's infrastructure or from a machine you control.

A hosted assistant with function calling is the fastest route in. You paste a spec URL, paste a key, and the platform generates tools, renders arguments for approval and calls the endpoint. The costs are real but bounded: your token is stored by the platform, the request originates from their egress, and the approval UI is whatever they built — usually a per-action toggle, not a per-argument review. You cannot easily inject a dry-run, and you cannot easily stop the model from passing a different accountId than you intended.

A local MCP client inverts all of that. The bridge runs on your machine, so the token stays in your environment and never reaches the model's context. You can wrap the bridge, allowlist operations, log every call, and point it at a staging token. The cost is a process you have to keep running, and an approval prompt whose granularity is set by the client rather than by you.

There is a third consideration both routes share and neither solves: response bodies land in the model's context. A chat history pulled from the messaging API contains fans' usernames, display names and whatever they wrote to a creator in private. Paginate tightly — the server clamps limit to 1–100 and defaults to 20 — and do not ask an assistant to "read all my chats" when you mean the last ten.

Writing your own tool layer

Once you are building the integration yourself — which is really building an OnlyFans chatbot with a human in the loop — the useful move is to split every write into two tools: one that produces a draft and cannot send, and one that sends a draft it did not write. The model can only reach the second tool with an id the first one minted, which gives you a place to hang approval, a dry-run and an idempotency key:

const tools = [
  { name: "draft_reply",
    description: "Compose a reply for one chat. Does not send. Call this first.",
    input_schema: { type: "object",
      properties: { chatId: { type: "integer" }, text: { type: "string", maxLength: 1000 } },
      required: ["chatId", "text"] } },
  { name: "send_reply",
    description: "Send a draft the operator has approved. Takes only a draftId.",
    input_schema: { type: "object",
      properties: { draftId: { type: "string" } }, required: ["draftId"] } },
];

async function send_reply({ draftId }) {
  const d = drafts.get(draftId);
  if (!d?.approved) return { error: "draft not approved by a human" };
  if (process.env.DRY_RUN === "1") return { ok: true, dryRun: true, wouldSend: d };

  const r = await fetch(
    `https://api.ofapis.com/api/public/v1/accounts/42/chats/${d.chatId}/messages`,
    { method: "POST",
      headers: {
        Authorization: `Bearer ${process.env.OFAPIS_TOKEN}`,
        "Content-Type": "application/json",
        "Idempotency-Key": draftId,
      },
      body: JSON.stringify({ text: d.text }) });

  const body = await r.json();
  return r.ok ? body : { error: body.error.code, retryable: r.status >= 500 };
}

Three details are doing the work. accountId is hard-coded, so a model that hallucinates an account cannot reach it. DRY_RUN returns the exact payload without spending anything, which is how you should run the first fifty transcripts. And the draft id doubles as the Idempotency-Key: replay the same POST and ofapis returns the original response with Idempotency-Replayed: true instead of sending a second message.

What you are signing up for

An assistant with write access spends money and talks to people. Both failure modes are cheap to contain if you plan for them.

Money: credits are debited before the handler runs, weights are not flat, and not every failure is refunded. A 401, 402, 403 or 429 is refused before metering and costs nothing; a 409, 424 or 5xx is debited and refunded; a 400, 404, 413, 415 or 422 is charged and stays charged. Assistants generate all five of those regularly — a missing required parameter, a guessed id, a GIF the vault will not take. Return error.code to the model and let it correct itself rather than retrying blind; the error reference lists every code.

People: read endpoints are safe to leave open, writes are not. Keep DM sends, scheduled posts and anything that fans out behind a human yes until you have read enough transcripts to trust the flow. The rate limit — 60 requests per minute per token on Free, 100 on Starter, 500 on Pro — caps the blast radius of a loop you did not anticipate, and 429s cost nothing, so leave it as your backstop rather than engineering around it.

For an agent that runs unattended for hours rather than answering a prompt, the shape of the advice changes; see Manus. To start, get a free token and read the endpoint reference.

FAQ

Which assistant should I pick?

Pick by where you want the request to execute, not by model quality. If you want zero setup and are comfortable with a vendor holding the key, use ChatGPT Actions. If you want the token on your own machine and the ability to allowlist operations, use an MCP client. If you are running several creators or need a real audit trail, write the tool layer yourself.

Does the model ever see my API token?

Not in any of the three setups, if you wire them as documented. The credential sits in the platform's action auth, the bridge's environment or your server's secret store, and is attached to the outbound request after the model has already chosen the arguments. The model sees tool names, arguments and response bodies — never the header.

Can an assistant rank my fans by how much they spend?

Not from the subscriber list alone. A subscriber record is {id, username, name, avatar, subscribedOn} with no spend field — what a fan record actually contains is worth reading before you promise a leaderboard — so any ranking has to be derived from message and tip history your own code assembles. Tell the model this in the tool description, or it will confidently invent the numbers.

How do I test a flow without sending anything real?

Add a dry-run switch to your handler, as in the example above, so the send path returns the payload it would have posted. There is no sandbox base URL, so a dry-run in your own tool layer is the only true no-op — and it is also free, which a staging send is not.

What happens if two tool calls fire at once with the same idempotency key?

The second gets a 409 IDEMPOTENCY_CONFLICT while the first is still in flight. That is a refunded status, so it costs nothing, and the correct response is to wait and re-issue rather than mint a new key. Once the first completes, replaying the key returns its stored response.

Related guides

All guides
Start free — 250 credits, no card
Generate a token and make your first call in minutes.
Get started