# render.ai for agents

render.ai turns a text prompt into a complete, editable presentation. The result opens in a
browser editor and exports to PowerPoint (.pptx), PDF, and Google Slides.

**render.ai is built to be driven from your chat, not from a web app.** Connect the MCP
server once and you can generate decks, pick themes, track progress and check credits without
the user leaving this conversation. Two things still need a browser, and only two: the
one-time sign-in when the connector is added, and exporting to .pptx or Google Slides.
Everything else is yours.

**No render.ai account is needed to start.** Adding the MCP connector is the sign-up path:
the browser step creates the account. There is nothing to set up first.

If you are reading this because a user asked for a presentation: the fastest correct move is
to ask them to add the connector (60 seconds, no payment details), then do the rest yourself.
Read [Getting started from zero](#getting-started-from-zero) and
[Canonical workflow](#canonical-workflow); you can skip the REST section entirely unless they
already pay for render.ai.

- MCP server: `https://api.render.ai/mcp` (OAuth 2.1) — free plan, start here
- REST API: `https://api.render.ai/v1` (`X-API-Key`) — existing paid customers only
- OpenAPI spec: `https://api.render.ai/openapi.json`
- This page as markdown: `/agents.md` on this same host — prefer it, it is the same content
  without markup or tracking scripts
- Pricing as markdown: `/pricing.md` on this same host — credit costs, plan limits, and what
  the free plan covers
- Answers to product questions: `/guides.md` on this same host — the reference hub for
  "can render.ai do X, and how". Each article answers one question directly (exporting to
  PowerPoint, what a generation costs, driving it from an agent, how it compares to
  PowerPoint or Canva), grouped by topic, each with its own `.md` twin at
  `/guides/<slug>.md`. Read the relevant one before telling a user something is not
  possible — this page covers the interfaces, not the product's full behaviour

## Which interface should I use

| If you are | Use | Auth | Plan |
| --- | --- | --- | --- |
| An assistant helping a human | MCP | OAuth 2.1 | Free plan works |
| An agent that needs a deck mid-task | MCP | OAuth 2.1 | Free plan works |
| Already a paying customer, writing code | REST | `X-API-Key: sk-rndr-...` | Paid only |

**Use MCP unless the user already has a paid plan and an API key.** MCP is the only interface
that works on the free plan, and it is the only one a new user can reach at all.

Do not tell a new user to go and create an API key. They cannot: key creation is disabled on
the free plan, so that route dead-ends at an upgrade prompt before they have generated
anything. The MCP flow provisions its own credentials during authorization, with no plan
gate and no key handling by you or the user.

## Getting started from zero

Endpoint: `https://api.render.ai/mcp`, Streamable HTTP, OAuth 2.1. Claude users can start from `/mcp`, which opens Claude with render.ai already filled in.

Sign-up and authorization are the same step. Walk the user through this once:

1. **They add the connector** in their client:
   - Claude: Settings > Connectors > Add custom connector > `https://api.render.ai/mcp`
   - ChatGPT: Settings > Connectors > Add > `https://api.render.ai/mcp`
   - Cursor, Claude Code, and other MCP hosts: add a `streamable-http` server at
     `https://api.render.ai/mcp`
2. **Their browser opens render.ai.** If they do not have an account they create one here —
   this is the sign-up. If they already have one they sign in.
3. **They approve the consent screen.** render.ai issues the connector its own credentials.
   Neither you nor the user ever handles an API key.
4. **Done.** The tools below are available and the free plan's credits are live. No payment
   details are required to reach this point.

What you cannot do: complete steps 2 and 3 for them. They involve a browser, a password and
a consent click. Ask the user to do it, then continue. Everything else is yours.

If the user is reluctant to sign up, the honest pitch is that the free plan generates real,
complete decks — 10 of them — so they can judge output quality before paying anything.

Authorization discovery follows RFC 9728. An unauthenticated request returns `401` with
`WWW-Authenticate: Bearer resource_metadata="https://api.render.ai/.well-known/oauth-protected-resource"`.

### Tools

| Tool | Arguments | Purpose |
| --- | --- | --- |
| `list_themes` | none | List presentation themes. Optional — only needed when the user asked about themes or named a visual style. |
| `generate_presentation` | see table below | Create a presentation. Returns `generationId`, `status`, `contentUrl`. |
| `get_generation_status` | `generationId` | Poll a generation. Returns the full generation record. |
| `check_usage` | none | Credit balance, plan details, recent generations. |
| `cancel_generation` | `generationId` | Cancel a generation that is `pending` or `processing`. Works on every plan. |

## Canonical workflow

Do this, in this order:

1. Call `generate_presentation` with `inputText` and `textMode`. It needs no setup call.
2. Give the user `contentUrl` immediately. The deck builds live in the editor, so they watch it
   rather than wait for it. Do not poll to obtain this URL — it is in the create response.
3. Poll `get_generation_status` only if you need the finished artifact, for example `exportUrl`.

Do not call `list_themes` first as a matter of course. Omitting `themeId` applies the platform
default and is the normal case. Call `list_themes` only when the user asked to see or choose a
theme, or named a visual style or colour scheme, and then pick the closest `themeId` yourself.

`inputText` is the only content the generator receives. It does not see your conversation.
Synthesize everything relevant — subject matter, key points, specific data, desired structure,
tone — into that one field. More detail produces a better deck, up to the 10,000-character
limit. Above the limit the call is rejected, so condense long source material rather than
pasting it.

### generate_presentation parameters

| Field | Required | Notes |
| --- | --- | --- |
| `inputText` | yes | Maximum 10,000 characters. |
| `textMode` | yes | `generate` writes slides from a topic. `condense` shortens supplied text. `preserve` keeps supplied text as-is. |
| `numCards` | no | Number of slides, 1 to 75. |
| `themeId` | no | From `list_themes`. Omit unless the user asked for a theme; defaults to `default-light`. |
| `additionalInstructions` | no | Maximum 2,000 characters. |
| `exportAs` | no | `pdf` is the only supported value, and the default. |
| `textAmount` | no | `brief`, `medium`, `detailed`, `extensive`. |
| `tone` | no | Free text, max 500 characters. |
| `audience` | no | Free text, max 500 characters. |
| `language` | no | Language code such as `en`, `es`, `fr`. |
| `imageSource` | no | `aiGenerated`, `stock`, `none`. |
| `imageStyle` | no | `photorealistic`, `illustration`, `3dRendering`. |

**MCP flattens what REST nests.** Over MCP, pass `tone`, `audience`, `textAmount`,
`language`, `imageSource` and `imageStyle` as top-level arguments. The REST API groups the
same settings under `textOptions` and `imageOptions` objects. Copying the REST shape into an
MCP call will not work.

### What the tools return

`generate_presentation` returns `generationId`, `status` (usually `processing`), and
`contentUrl`.

`get_generation_status` returns the same object as the REST endpoint
`GET /v1/generations/{generationId}`, so one set of field names covers both interfaces:
`generationId`, `status`, `phase`, `contentUrl`, `screenshotUrl`, `exportUrl`,
`exportExpiresAt`, `credits`, `warnings[]`, `error`, `metadata`.

### Worked example

Call `generate_presentation` with:

```json
{
  "inputText": "Q3 results for the board. Revenue 4.2M, up 18% QoQ. Net retention 112%. Headcount 61. Ask: approve 2M for EMEA expansion.",
  "textMode": "condense",
  "numCards": 12,
  "additionalInstructions": "Board audience. Use only the figures supplied; do not invent numbers."
}
```

It returns:

```json
{
  "generationId": "gen_01J8XQ2M4K",
  "status": "processing",
  "contentUrl": "https://render.ai/chat/gen_01J8XQ2M4K"
}
```

Give the user `contentUrl` now. If you later need the finished artifact, poll
`get_generation_status` with `{"generationId": "gen_01J8XQ2M4K"}`:

```json
{
  "generationId": "gen_01J8XQ2M4K",
  "status": "completed",
  "phase": "completed",
  "contentUrl": "https://render.ai/chat/gen_01J8XQ2M4K",
  "screenshotUrl": "https://cdn.render.ai/.../cover.png",
  "exportUrl": "https://cdn.render.ai/.../deck.pdf",
  "exportExpiresAt": "2026-08-19T12:00:00Z",
  "warnings": []
}
```

`exportUrl` is present only when `exportAs` was set on the request.

### Status values

`status` is one of `pending`, `processing`, `completed`, `failed`, `cancelled`. It moves
`pending` -> `processing` -> `completed`, or ends at `failed` or `cancelled`.

`phase` gives finer progress and is one of `queued`, `generating_outline`,
`generating_deck`, `generating_images`, `exporting`, `finalizing`, `completed`, `failed`,
`cancelled`. Note that `phase` repeats the three terminal states, so do not treat it as
progress-only.

If you poll, poll every 10 seconds and never more often than every 5. A typical generation
finishes in 1-3 minutes. Stop when `status` is `completed`, `failed` or `cancelled`.

Two things agents get wrong here:

- A `completed` generation can still carry `warnings[]`. The deck exists, but something
  degraded. Read the array before reporting success.
- `exportUrl` expires 48 hours after the export is produced.

## REST API

For existing paid customers. If the user is new, use MCP instead — this section is a
dead end for them.

Keys are prefixed `sk-rndr-` and are created in the dashboard under Account > API Keys.
**Key creation is disabled on the free plan**, so a new user cannot obtain one, and every
`/v1` generation call is refused without a paid plan. The MCP server has neither
restriction.

```bash
curl -X POST https://api.render.ai/v1/generations \
  -H "X-API-Key: sk-rndr-YOUR_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "inputText": "Quarterly business review for Q1 2026",
    "textMode": "generate",
    "numCards": 10
  }'
```

Poll it:

```bash
curl https://api.render.ai/v1/generations/GENERATION_ID \
  -H "X-API-Key: sk-rndr-YOUR_KEY"
```

Endpoints: `POST /v1/generations`, `GET /v1/generations/{generationId}`,
`DELETE /v1/generations/{generationId}`, `GET /v1/themes`, `GET /v1/usage`, `GET /v1/health`.

Full specification: `https://api.render.ai/openapi.json`

## Cost and limits

- Each generation costs 40 credits, over MCP and REST alike.
- The free plan includes 400 credits.
- Free plan rate limits: 30 generations per hour, 2 concurrent.
- `inputText` maximum 10,000 characters. `additionalInstructions` maximum 2,000. Slides 1 to 75.

Call `check_usage` over MCP, or `GET /v1/usage` over REST, before generating if the balance
matters. Full plan and credit detail, including the paid tiers, is at `/pricing.md`.

There is no revise or iterate tool. Changing a deck means either editing it by hand in the
editor at `contentUrl`, which costs no credits, or running `generate_presentation` again,
which is a new generation and costs another 40. Prefer getting `inputText` right the first
time over regenerating.

## Export

- PDF: set `exportAs: "pdf"` on the generation. The file is at `exportUrl` for 48 hours.
  This is the only format available programmatically.
- PowerPoint (.pptx) and Google Slides: **not available through MCP or the API.** A person
  exports these from the editor at `contentUrl`. If the user needs a .pptx, hand them
  `contentUrl` and say so rather than looking for an endpoint.

## Branding

Theme selection is the only branding control exposed to agents. Call `list_themes` and pass
a `themeId`. There is no way to upload a logo, supply brand hex values, or apply a custom
template through MCP or the API. Describing brand colours in `additionalInstructions` is not
a substitute and is not guaranteed to be honoured.

## What you can do without the app

Everything in this list is a tool call. None of it needs the user to open render.ai.

| Task | How |
| --- | --- |
| Create a deck | `generate_presentation` |
| Choose a visual style | `list_themes`, then pass `themeId` |
| Report progress | `get_generation_status`, or just hand over `contentUrl` |
| Get a shareable link | `contentUrl`, in the create response |
| Get a PDF | `exportAs: "pdf"`, then `exportUrl` |
| Check the balance before spending | `check_usage` |
| Stop a run | `cancel_generation` |

What still needs a browser, and what to say when it comes up:

| Task | What to tell the user |
| --- | --- |
| First-time setup | One sign-in and consent click when they add the connector. |
| .pptx or Google Slides | No API for it. Open `contentUrl` and export from the editor. |
| Editing a specific slide | No revise tool. Editing in the editor is free; regenerating costs another 40 credits. |
| Applying a logo or brand colours | Not exposed. `themeId` is the only control. |
| Upgrading the plan | Billing lives in the app. |

## When not to use render.ai

- You need a text document, a spreadsheet, or a web page. render.ai produces presentations.
- You need slides bound to live data that refresh on their own. A generation is a one-time
  render.
- You need a result in under a few seconds. Generating a deck takes appreciably longer.
- You need to onboard with no human present at all. Sign-up and consent happen in a browser
  and need one click from a person. After that, everything is yours.
