Skip to content

Documentation

The Plug-in AI API

Make images, video and speech from your own product, at the same prices and under the same rules as the studio. Every call acts as one person in one workspace: it sees what their seat can see, spends that workspace's credits, and bills the work to a client and a job.

Getting access

API access is not self-serve yet. Today there are two ways in.

A platform built on Plug-in
We issue one partner credential for your whole platform. Each account you serve gets its own workspace here, created by your platform through that one credential, and every call names the workspace and the person it acts for. Nobody keeps a key per account.
Claude, ChatGPT and other assistants
An assistant connects with sign-in, not a key: the person signs in to Plug-in, picks a workspace and allows it. The connector guide covers it step by step.

To build on the API, write to info@plug-in.ai with what you are making and roughly how much you expect to generate. We set up your credential and its spending limits with you.

connector guide →

Signing requests

Base address

https://plug-in.ai

Every private call

Authorization: Bearer <credential>

  • Send the credential as a bearer token in the Authorization header. Never put it in an address, a log or code that runs in a browser.
  • A partner credential also names who it acts for on every call: the Plugin-Workspace header (the workspace's id) and the Plugin-Actor header (your own reference for the person).
  • A call acts as that person: their role, their client access and their workspace's credits. Removing the person, revoking the credential or suspending the workspace ends access on the next call.
  • Creating clients, jobs and uploads and generating need an admin or creator seat. A client account can only read its own client's work.
  • Bodies are JSON. Request fields are snake_case; record fields in answers are camelCase.

Your first generation

Every generation is billed to a client and a job. Create them once, then quote, generate and read the result.

  1. Create a client. Send your own external_ref so asking again returns the same client instead of a second one.

    POST /api/clients {"name": "Example company", "external_ref": "acme:company:123"}
  2. Create a job for that client, the same way.

    POST /api/jobs {"client_id": "<client.id>", "code": "RUN-123", "name": "Campaign", "external_ref": "acme:run:123"}
  3. If the work needs a reference file, ask for an upload ticket, PUT the exact bytes to its upload_url with its required headers, and use its file_url in the generation.

    POST /api/uploads/reference {"content_type": "image/png", "size_bytes": 123456}
  4. Choose a model from the public catalogue, then price the exact request and show the person the most it can cost.

    GET /api/public/models · POST /api/generations/quote
  5. Generate with an Idempotency-Key, and the quote's confirmation if you quoted it.

    POST /api/generations/sync Idempotency-Key: acme:run:123:image:1
  6. Read the generation until it is completed, failed or canceled, then fetch its outputs with the same credential.

    GET /api/generations/<id> · GET /api/media/<id>?i=0

Quote before you spend

  • POST /api/generations/quote prices one to four requests and spends nothing: nothing is reserved, generated or charged.
  • Each item answers maximum_credits, the most that request can ever charge, and a quote_fingerprint for exactly those settings, references, client, job and prices.
  • Send the same request to /api/generations/sync with quote_version, quote_fingerprint and approved_max_credits, and it starts only at exactly what was quoted. If anything changed, nothing starts and nothing is charged: 409 quote_changed, or approved_price_exceeded when the price rose.
  • With a partner credential the confirmation is optional. With sign-in from an assistant it is required: new work without it is refused (428 approval_required) before anything is reserved.
  • Whatever happens, a generation is never charged above the maximum frozen when it was accepted.

Safe retries

  • Send an Idempotency-Key with every generation: a string of 1 to 200 characters you choose, one for each piece of approved work.
  • The same key with the same work returns the same generation (Idempotency-Replayed: true), never a second one. Keys are kept for at least 24 hours and never reused after that.
  • The same key with different work is refused (409 idempotency_conflict). A new key is new work, and may be charged again.
  • If an answer is lost, send the identical request with the same key, or read the generation you already have. Never make a new key to recover.
  • external_ref on clients, jobs and brand toolkits works the same way: asking again returns the existing record, unchanged.

Credits and money

  • You pay normal Plug-in prices in credits from the workspace's wallet. There is no separate price for the API.
  • platform_cost_cents is a number of credits, despite its name. It is not money: do not divide it by 100 or show it as dollars.
  • billable_cost_cents is the agency's own invoice amount to its client, in its invoice currency, and a different thing.
  • A cost not yet known is null, never a guess. Read the generation again until it is set.
  • A client can be given a ceiling in credits (budget_cents). Work that would cross it, or that the wallet cannot cover, is refused before anything starts (402).
  • GET /api/generations with from and to returns a statement for any period, with totals over the whole period.

Pricing →

Reading results

  • There are no webhooks or callbacks: poll GET /api/generations/<id>. Poll calmly, every few seconds, and honour Retry-After when it is sent.
  • For video send prefer_async: true. The answer is the generation's id at once, and the video carries on without your request staying open.
  • A 202 answer means accepted or queued, not ready. Keep the id it gives you.
  • Outputs are fetched from /api/media with the same credential; a link alone grants nothing. Video accepts Range requests for seeking.
  • Every generation carries its client, job and brand toolkit, and a stable provenance id you can store.

Errors

Read the error code in the answer, not only the HTTP status. A refusal is about money, permission or the request itself, and sending it again unchanged will not fix it.

CodeWhat it means
400 invalid_*A field is wrong or the model does not take that setting. Nothing was charged.
401The credential is missing, wrong, expired or revoked.
402 insufficient_creditsThe workspace's wallet cannot cover the work.
402 client_budget_exhaustedThe work would cross the client's ceiling.
403 plan_requiredThe workspace's plan does not include this model or tool.
403 org_suspendedThe workspace is suspended.
403 insufficient_scopeThe sign-in was not allowed to make changes.
409 idempotency_conflictThat key was already used for different work.
409 quote_changedThe request or its price changed since the quote. Quote again.
409 approved_price_exceededIt now costs more than was approved.
428 approval_requiredAn assistant must send a binding quote's confirmation.
503 capacity_unavailableBusy for now. Read what you already have before sending anything new.

A model can decline a request for what it shows. A declined generation is never charged; why a generation is declined.

There is no fixed limit on requests a minute. Paid work is admitted as capacity and credits allow, so accepted work can wait in a queue.

Endpoints13

Every endpoint in the contract, generated from it. The OpenAPI document has every field, limit and answer.

OpenAPI document →Model catalogue →

Public2

MethodPathWhat it does
GET/api/public/openapi.jsonThis contract, as an OpenAPI 3.1 document. No sign-in.
GET/api/public/modelsThe models you can call, their settings and prices. No sign-in.

Clients, jobs and brands4

MethodPathWhat it does
POST/api/clientsCreate a client, or return the one with the same external_ref.
PATCH/api/clients/{id}Change a client's details or its credit ceiling (admins).
POST/api/jobsCreate a job for a client, or return the one with the same external_ref.
POST/api/brand-kitsCreate a brand toolkit to bill work under, or return the existing one.

Files2

MethodPathWhat it does
POST/api/uploads/referenceGet a ticket to upload a reference picture, clip or sound.
GET/api/media/{generationId}Fetch an output's bytes.

Generation4

MethodPathWhat it does
POST/api/generations/quotePrice one to three requests. Spends nothing.
POST/api/generations/syncStart one approved generation.
GET/api/generations/{id}Read a generation: status, outputs, attribution and cost.
GET/api/generationsList generations, or a cost statement for a period.

Tools1

MethodPathWhat it does
POST/api/mcpThe same tools the assistants use, over JSON-RPC.

Help

Questions about the API, a credential or a refusal: write to info@plug-in.ai. Building for Claude or ChatGPT instead? Read the connector guide.

Launch news, twice a month

New models and plugins the day they land. No spam, one click to leave.