Every API here is available over the APIs.io API and to AI agents over MCP.
openapi: 3.2.0
info:
title: Rendry Capabilities API
version: 1.6.0
description: 'Template-based image generation API. Design a template in the visual editor (or via POST /presets), then render on-brand images with one call. Every capability the MCP server exposes as a tool is reachable over REST as well — same implementation behind both, so the two never drift. For AI agents a remote MCP endpoint is available at /mcp (streamable HTTP) — see https://rendry.io/llms-full.txt. Every refusal, on every door, is one JSON object {error: machine code, message: sentence, …fields} with the HTTP status of its class — see the Refusal schema; an MCP tool error carries the same object as its text.
Authentication: four bearer credentials open the render and template doors — a persisted live or test API key (sk_live_… / sk_test_…), the browser session of the signed-in owner (the JWT POST /auth/login returns) a connector token (the JWT the OAuth flow issues to an MCP client), and an ephemeral editor session (em_…). Browser/OAuth credentials may select a current team membership with X-Rendry-Account; API keys and embed sessions are bound to their persisted account and cannot switch accounts. GET /principal returns the resolved authority. Roles and scopes intersect; a removed member immediately loses access. Test mode is persisted on the credential: renders retain all admission limits, cost zero credits and carry a RENDRY TEST watermark; test credentials cannot publish public links. Operations that list the `session` scheme alone are the account cabinet: an API key is refused there with 401, a connector token with 403 `browser_session_required` (account deletion: `account_deletion_needs_session`). Operations with an empty `security` are public.
Also served, not described operation by operation: GET /templates/{id}/preview.png and GET /template-assets/{name} (public; the gallery''s pictures), GET /assets/library (public; the built-in image stock), GET /plans, GET /billing/config and GET /version (public; prices and the running build), GET /openapi.json (public; this document), POST /feedback (public, rate-limited by IP) and POST /feedback/agent (render credentials), the OAuth 2.1 doors under /oauth (the three /.well-known discovery documents, /register, /authorize, /client-info, /token — public; POST /oauth/decision takes the owner''s session), the guest batch under /batch-tasks/guest (public, rate-limited by IP: POST /preview, POST, GET /{token}, GET /{token}/archive), the sign-in, sign-up, e-mail verification and password doors under /auth (public; POST /auth/verify/resend takes the owner''s session), the cabinet''s POST /user/password, POST /checkout, GET /billing/history, GET /billing/subscription, GET /photos/search and POST /photos/used (owner''s session only), the payment webhooks POST /billing/webhook and POST /billing/webhook/{provider} (signed by the provider), the /internal/* doors of the render nodes (internal token) and the /admin group (administrators, session only).'
contact:
url: https://rendry.io
servers:
- url: https://rendry.io/api/v1
description: Production
security:
- apiKey: []
- session: []
- connector: []
- embed: []
tags:
- name: Capabilities
paths:
/capabilities:
get:
summary: Service capabilities
description: Layer types, available fonts, canvas limits, max layers. The whole reference by default; ?sections= narrows it to the named sections, the same names get_capabilities takes.
security: []
parameters:
- name: sections
in: query
required: false
description: Section names, repeated or comma-separated. Omit for the whole reference. An unknown name is reported in sections_note with the list of valid ones.
schema:
type: string
responses:
'200':
description: Capabilities
content:
application/json:
schema:
$ref: '#/components/schemas/Capabilities'
operationId: getCapabilities
tags:
- Capabilities
components:
schemas:
VariableLimits:
type: object
description: The resource contract of one effective context (one render, one batch row, one document page). Each limit is checked BEFORE the work it bounds; passing one is a named refusal (source_limit, node_limit, depth_limit, numeric_limit, output_limit) before any charge. A template without a template or formula definition is never judged by these; the text caps still apply to it.
properties:
names:
type: integer
description: definition, supplied-input and fact names of one context, once a template or formula is active
name_bytes:
type: integer
description: a name in an explicit mode and every reference; names are ASCII
value_code_points:
type: integer
description: one source, supplied value or expanded value, in Unicode scalar values — the same cap as max_text_chars
aggregate_source_bytes:
type: integer
description: all definition sources plus supplied values, UTF-8
definition_nodes:
type: integer
description: formula AST nodes or template segments of one definition
aggregate_nodes:
type: integer
description: their sum over the active definitions
fuel:
type: integer
description: evaluation work of one context, in units of instructions, emitted segments, digit parses and arithmetic operand words
depth:
type: integer
description: the longest dependency chain below a definition
nesting:
type: integer
description: parentheses plus unary signs inside one formula
aggregate_output_bytes:
type: integer
description: all computed text of one context, UTF-8
token_digits:
type: integer
description: digits of one decimal operand, leading and trailing zeros included
rational_bits:
type: integer
description: a reduced numerator and a reduced denominator, each
scratch_bits:
type: integer
description: every intermediate product or sum of the arithmetic
format_scratch_bits:
type: integer
description: the scaled integers of a decimal projection
Capabilities:
type: object
properties:
bind_measured:
type: boolean
description: 'This build supports formulas over measured sibling layers in image presets. Full rules and recipes: GET /design-guide?section=bind. Documents refuse.'
bind_measured_metrics:
type: array
items:
type: string
description: Supported metric names, generated from the engine measurement table.
finish_profiles:
type: array
items:
$ref: '#/components/schemas/CanvasFinishProfile'
description: Whole-canvas finishes generated from the runtime registry.
nested_variables:
$ref: '#/components/schemas/NestedVariables'
description: Present only when this build interprets variable_modes; a client gates authoring of templates and formulas on it and never downgrades them silently. Without ?sections= the whole struct is served, limits included; ?sections=nested_variables serves it alone. MCP get_capabilities carries grammar_version in its working set and the rest under that section.
service_policy:
type: object
description: Typed rate, concurrency, lifetime and analytics-header contract; mirrors x-rendry-service-policy and is available through sections=service_policy.
additionalProperties: true
additionalProperties: true
CanvasFinishProfile:
type: object
required:
- id
- field
- label
- description
- formats
- cost_class
- min_dimension
- requires_transparent
properties:
id:
type: string
description: Exact versioned value accepted by Canvas.finish_profile. Values are generated from the runtime finish registry.
field:
type: string
description: Public DSL path where the id is written.
label:
type: string
description:
type: string
formats:
type: array
items:
type: string
description: Output formats accepted by this profile.
cost_class:
type: string
min_dimension:
type: integer
description: Minimum final width and height in pixels.
requires_transparent:
type: boolean
description: Whether the profile requires a fully transparent canvas background.
NestedVariables:
type: object
required:
- grammar_version
properties:
grammar_version:
type: integer
description: The template and formula grammar, the exact arithmetic and the text projection this build interprets. Compare it with the version your client was written for; refuse to author modes on a mismatch.
projection_digits:
type: integer
description: 'Precision boundary: the significant digits a repeating decimal is rounded to (nearest, halves away from zero) when a formula result becomes text. A terminating decimal prints in full; a formula reading another formula receives the exact value, not the projection.'
limits:
$ref: '#/components/schemas/VariableLimits'
securitySchemes:
apiKey:
type: http
scheme: bearer
description: 'API key sk_live_... or sk_test_... — create at Settings → API Keys. Every key has a scope (read, render or full — see the APIKey schema); a call the scope does not cover answers 403 with `error` = `api_key_scope_insufficient`, the refused `tool`, the key''s `scope`, and a `message` naming what the key can call. A key limited to a list of presets answers a preset outside it with 403 `api_key_preset_not_allowed` and the refused `preset_id`; such a key renders and edits only its listed presets, and an inline design (canvas + layers, a document page or master of its own), a draft or a new template is refused with 403 `api_key_limited_to_presets` and a `reason` (inline_design, draft, create_template, inline_page). RESERVED: no door issues a key limited to presets yet — POST /user/api-keys hands out keys for the whole account — so `api_key_preset_not_allowed` and `api_key_limited_to_presets` are documented for clients built ahead of that door and are not answered by any key issued today.'
session:
type: http
scheme: bearer
bearerFormat: JWT
description: 'The browser session of the signed-in owner: the JWT POST /auth/login (or the SSO door) returns, sent as `Authorization: Bearer <jwt>`. Opens every render and template door and, alone, the account cabinet (API keys, account deletion, password, billing).'
connector:
type: http
scheme: bearer
bearerFormat: JWT
description: 'The token the OAuth 2.1 flow under /oauth issues to an MCP client (claude.ai connectors): the same JWT shape as a session, tagged as issued to a connector. Opens the render and template doors and /mcp; every cabinet door refuses it with 403 `browser_session_required`, account deletion with 403 `account_deletion_needs_session`.'
embed:
type: http
scheme: bearer
description: Ephemeral em_... token redeemed by the origin-restricted embedded editor. Its account, trusted subject, role and live/test mode are persisted server-side. It cannot access account administration, billing, account-wide batch services or public links. Never persist or put it in a URL.
x-rendry-service-policy:
rates:
rest_render:
requests: 10
window_seconds: 1
burst: 10
scope: account
consistency: api_process
mcp_request:
requests: 10
window_seconds: 1
burst: 10
scope: account
consistency: api_process
ui_request:
requests: 10
window_seconds: 1
burst: 10
scope: account
consistency: api_process
public_link:
requests: 60
window_seconds: 60
burst: 60
scope: ip
consistency: api_process
embed_frame:
requests: 10
window_seconds: 60
burst: 10
scope: ip
consistency: api_process
embed_redeem:
requests: 10
window_seconds: 60
burst: 10
scope: ip
consistency: api_process
sync_concurrency:
account: 4
subject: 2
consistency: api_process
scope: synchronous_render
document_queue:
pending_per_account: 4
long_pending_per_account: 1
long_above_pages: 20
consistency: postgres_transaction
scope: account; queued and processing documents
retention:
authenticated_days: 365
guest_hours: 72
url_cache_days: 30
starts_at: 'authenticated: completion; guest: batch creation'
end_user:
header: X-End-User
max_bytes: 64
pattern: ^[A-Za-z0-9_.:@-]{1,64}$
authority: analytics only; trusted embedded subject takes precedence
notes:
- REST, MCP and UI request buckets are separate; each account bucket is shared by members, keys and embedded subjects
- API keys bypass only the UI bucket; authenticated embedded sessions use it
- Sync concurrency is per API process; queued document admission is separately reserved in PostgreSQL
- Retention is the source policy; deployment of storage lifecycle rules requires a separate operator action