Plyry Developer API · Version 1
Plyry API
Use the Plyry API from a server, desktop app, game, or website. It returns JSON, accepts standard media uploads, and does not require a Plyry page to be embedded in your app.
https://plyry.com/v1
Requests use HTTPS. Authenticated endpoints use bearer tokens. API requests do not use the website session cookie.
Authentication
Use an API key for server-to-server access. Create one in Account → API keys. The full key is shown once. Store it in a secret manager, and send it in the Authorization header.
Authorization: Bearer plyry_api_…
For an app that signs users in directly, call the API login or signup endpoints. They return short-lived access tokens and rotating refresh tokens. A successful login or signup creates an entry in Account → Linked apps, where the user can disconnect it.
Authentication model: these endpoints send the user's Plyry email and password directly to Plyry and issue API tokens. This is not OAuth or single sign-on. Use it only in an app you operate, never forward a Plyry password to another service, and keep refresh tokens protected. It does not add email verification or account recovery.
Browser requests are restricted to the origin used when the user signs in. Native and server clients can omit an origin. Request only the scopes the app needs.
If scopes is omitted, a linked app receives account and generation permissions. The model catalog is public. Wallet access is opt-in: request wallet:read to read balances and wallet:topup to add funds.
Conversion tools use conversion:create to price, submit, or revise a conversion and conversion:read to inspect its status, search its catalog, and download results.
Sign in
POST /v1/auth/login
Content-Type: application/json
{
"email": "person@example.com",
"password": "their-password",
"app_name": "My media app",
"scopes": ["account:read", "generation:create", "generation:read"]
}
For a new user, first request a signup challenge, then submit the answer and signup details.
POST /v1/auth/signup/challenges
{"email":"person@example.com"}
POST /v1/auth/signup
{
"email": "person@example.com",
"password": "a-long-password",
"app_name": "My media app",
"signup_challenge": "challenge-from-the-previous-response",
"answer": "12"
}
Access tokens expire after 15 minutes. Exchange the refresh token at POST /v1/auth/refresh; each successful exchange rotates both tokens. Call POST /v1/auth/revoke with the refresh token when the user signs out.
API key scopes
account:read— read the signed-in account.generation:create— request prices and create generations.generation:read— read generations and download their results.wallet:read— read the wallet balance.wallet:topup— create wallet top-ups.conversion:create— price, submit, and revise supported conversions.conversion:read— read conversion reports and download results.
Websites and CORS
The API supports requests from other HTTPS websites. Sign in from the website itself to link the account; Plyry binds the resulting token to the browser's website origin. For API keys, add the website origin to the key in Account → API keys.
Send bearer tokens in the Authorization header and use credentials: "omit" with Fetch. Do not put server API keys in public page source. Browser-bound keys are origin-restricted, but users should still treat them as exposed to code running on that origin.
const response = await fetch("https://plyry.com/v1/me", {
headers: { Authorization: `Bearer ${accessToken}` },
credentials: "omit"
});
Account and wallet
/v1/meRequires account:read/v1/walletRequires wallet:read/v1/wallet/ledgerRequires wallet:readWallet amounts use integer cents. The wallet endpoint also returns the configured top-up limits.
Models and quotes
/v1/modelsPublicThe catalog lists available media types, model IDs, reference modes, and generation settings. Provider credentials and internal routing are not returned.
/v1/quotesRequires generation:create{
"type": "image",
"model": "public-model-id",
"quality": "full",
"reference_mode": "",
"queue_options": {"generation": {"aspect": "1:1"}}
}
The response includes price_cents and has_sufficient_funds. Read the exact balance from GET /v1/wallet with the wallet:read permission. The generation request rechecks the current price before charging.
Generations
/v1/generationsRequires generation:create/v1/generationsRequires generation:readList the account's generations with page (default 1), per_page (default 20, maximum 50), and optional status and type filters.
Send JSON for prompt-only generation. Use multipart/form-data when attaching reference files. Multipart reference fields are references[] and matching reference_slots[] values from 1 to 7.
POST /v1/generations
Content-Type: application/json
{
"type": "image",
"model": "public-model-id",
"prompt": "A quiet greenhouse at sunrise",
"quality": "full",
"queue_options": {"generation": {"aspect": "1:1"}}
}
A successful request returns 201 with a job hash. Poll the job until it completes.
/v1/generations/{hash}Requires generation:readCompleted jobs include an absolute media_url. Send the same bearer token to download it. Splat jobs may also include a scene_url.
After saving a delivered file, confirm the verified copy so Plyry can release its server copy:
POST /v1/generations/{hash}/cache
{"cache_token":"token-from-media-response","sha256":"sha256-from-media-response"}
Plant 3D to Revit conversion
This conversion accepts a Plant 3D PCF file and a Revit fabrication catalog exported by the Plyry Revit add-in. The current conversion price is returned before upload. The service is in tester preview and requires a Plyry tester or admin account.
/v1/conversions/plant3d-to-revit/priceRequires conversion:create/v1/conversions/plant3d-to-revit/jobsRequires conversion:createSubmit multipart/form-data fields pcf and catalog. Use a fresh Idempotency-Key UUID for each intended conversion, and reuse that key only when retrying the same files; this prevents a network retry from charging twice. The PCF limit is 80 MB and the catalog limit is 20 MB.
/v1/conversions/plant3d-to-revit/jobs/{hash}Requires conversion:read/v1/conversions/plant3d-to-revit/jobs/{hash}/buttons?q=pipeRequires conversion:read/v1/conversions/plant3d-to-revit/jobs/{hash}/mappingRequires conversion:create/v1/conversions/plant3d-to-revit/jobs/{hash}/downloadRequires conversion:readPoll the job until it reaches review or complete. A mapping revision accepts a mappings object keyed by component signature and containing Revit target keys. Download the completed JSON package and apply it in Revit. Revising an existing job does not charge again.
Use only the conversion and wallet permissions the client needs. A desktop converter can request account:read, wallet:read, wallet:topup, conversion:create, and conversion:read.
Wallet top-ups
/v1/payments/configRequires wallet:topupThis returns the active processor's public configuration. Use its client-side tokenization flow with the public key to create a payment method ID, then send that ID to Plyry. Never send raw card numbers or CVCs to Plyry.
/v1/wallet/topupsRequires wallet:topupSend a fresh UUID in the Idempotency-Key header for each intended top-up. Reuse it only for retries of the same request. Plyry saves the request before contacting the payment provider so a lost response cannot silently start a second payment.
{
"amount_cents": 2000,
"payment_method_id": "payment-method-from-processor",
"billing_country": "GT",
"return_url": "https://app.example.com/account/payment-return"
}
/v1/wallet/topups/requests/{requestKey}Requires wallet:topupUse this endpoint after a lost response or an uncertain payment result. A 202 response means the payment is still processing or needs customer action; keep using the same request key and do not start a new top-up until the status is known. Poll this endpoint after completing provider verification. A terminal 200 response reports the result for that top-up.
For ONVO, return_url may use an origin allowed for the token. Native linked apps with no website origin may use an HTTP loopback callback at 127.0.0.1 on an ephemeral port, with a random path shaped like /return/{32 lowercase hex characters}. The fixed return_url from GET /v1/payments/config is available as a fallback. If the response includes payment.requires_action: true, open payment.action_url and poll the top-up request endpoint with the same request key after verification. You may also send the payment reference as payment_intent_id to POST /v1/wallet/topups/reconcile for explicit server-side reconciliation. Return query parameters alone do not prove payment. These endpoints return payment state only; read the wallet balance separately with wallet:read.
Errors
Errors return JSON with ok: false, a stable error code, and a readable message. Common HTTP statuses are 400 for invalid input, 401 for an invalid or expired token, 403 for insufficient scope or a blocked website origin, 402 for insufficient wallet funds, and 429 for rate limits.
{
"ok": false,
"error": "insufficient_scope",
"message": "This API token does not have the required permission.",
"required_scope": "generation:create"
}
OpenAPI specification
Download the machine-readable API contract as JSON: OpenAPI v1 specification.