You have code

Give your agent a render tool.
It signs itself up.

JSON in, PDF out. Every render deterministic, cached, and signed.

Start here

One address, and your assistant can make documents.

Galley is a hosted MCP server. Paste the address into Claude, ChatGPT, Claude Code or Cursor and the assistant gets 16 tools. It can render any of the 43 starter templates, create and version templates of its own, and check usage. It can also call create_account to get its own key without sending you off to a web page.

MCP endpoint

https://mcp.galleyrender.com/mcp

Streamable HTTP, stateless, and no auth needed to start. The first render call mints a trial of 10 PDF pages or 10 images and returns its token.

Not writing code? Pick a document and see it filled in with your own data →

Two clicks, and no terminal

Copy the address above, paste it into your assistant's connector settings, and ask for a document. That's all the setup there is. The trial is per connection and doesn't need a card, a key or a sign-up. When you want to keep the work for good, the assistant calls create_account with your address and you click one link.

Four assistants, and the steps for each

Every step below is quoted from the product's own documentation. Pick the one you use.

Galley's address https://mcp.galleyrender.com/mcp

Streamable HTTP, with no sign-in, key or OAuth. Your first documents are free. When you want more, ask your assistant to make you an account. You'll get an email that shows who asked and a short code, and you confirm it there. Already have a key? The last step in every tab below is where it goes: a header for Claude Code and Cursor, and, for Claude.ai and ChatGPT, a personal address plus the link_account tool.

Claude.ai

The web app at claude.ai, and Claude Desktop. Free, Pro, Max, Team and Enterprise. On Team and Enterprise an owner adds it once for the organization, then everyone connects to it.

  1. Open claude.ai → Customize → Connectors.
  2. Click +, then Add custom connector.
  3. Paste your personal address into the URL field. It's made here, in your browser, and nobody else has it:
    https://mcp.galleyrender.com/mcp/c/…
  4. Leave Advanced settings alone. That's where an OAuth client id and secret would go, and Galley doesn't need either.
  5. Click Add. Galley's tools are now in the tools menu of any new chat.
  6. Try it: ask for one of the documents below and watch it approve the tool call.
  7. Use your key. Claude.ai's connector dialog can't send an HTTP header, so the key goes in through a tool instead. Ask Claude: run link_account with my Galley key. If you'd rather not paste a key into a chat, ask it to run create_account with my email address. An address that already has an account gets a one-time code by email instead of a second account, and link_account takes that code. Check it worked with run whoami. This works because of your personal address: Claude calls Galley from Anthropic's servers, so on the shared address every Claude user looks the same, and that one can't be linked to anybody's account.
    what to say text
    Run link_account with this key: glr_sk_…
    
    or, with no key to hand:
    
    Run create_account with my email, then link_account with
    the code that arrives.
A drawing of the Claude.ai Connectors screen with the custom connector dialog open and the URL field highlighted. Customize › Connectors + Add custom connector Remote MCP server URL mcp.galleyrender.com/mcp/c/… Advanced settings — OAuth client id and secret Not needed: Galley asks for no sign-in. Add
Customize → Connectors, with the custom connector dialog open and the URL field highlighted.

On Team or Enterprise the first step is an owner's job: Organization settings → Connectors → Add → Custom → Web. Everyone else then finds it under Customize → Connectors and clicks Connect.

Keep your personal address to yourself. Once it's linked, whoever has it is using your account, the same as with a key. Don't paste it into a shared document. If somebody else might have seen it, ask for unlink_account and add Galley again with a new one. On a Team or Enterprise plan, an address an owner adds for the organization is shared by everyone in it.

ChatGPT

The ChatGPT web app. Plus, Pro, Business, Enterprise and Education, on the web. ChatGPT's free plan can't add a custom connector. On a free account, use Claude, which can.

  1. In ChatGPT, open Settings → Security and login and turn on Developer mode.
  2. Go to chatgpt.com/plugins.
  3. Click the + button and create a developer-mode app for a remote MCP server.
  4. Paste your personal address as the server URL. It's made here, in your browser, and nobody else has it:
    https://mcp.galleyrender.com/mcp/c/…
  5. Leave authentication empty, because Galley doesn't need any.
  6. Save. Your new app appears under Drafts. It's yours to use, and nothing is submitted to OpenAI or reviewed by anyone.
  7. In a chat, enable it and ask for a document. ChatGPT asks you to confirm the first time it makes a file. That prompt is normal for any action that writes something.
  8. Use your key. Leave the authentication fields empty (Galley doesn't use OAuth) and hand the key over with a tool instead: run link_account with my Galley key. No key to hand? Run create_account with my email address. If that address already has an account, we email it a one-time code, and link_account takes the code. Run whoami confirms which account you're on. It works because of your personal address: ChatGPT calls Galley from OpenAI's servers, so on the shared address every ChatGPT user looks the same, and that one can't be linked to anybody's account.
    what to say text
    Run link_account with this key: glr_sk_…
    
    or, with no key to hand:
    
    Run create_account with my email, then link_account with
    the code that arrives.
A drawing of the ChatGPT developer mode toggle and the new app dialog, with the MCP server URL field highlighted. Settings › Security and login Developer mode on chatgpt.com/plugins + New app MCP server URL mcp.galleyrender.com/mcp/c/… Authentication: none. Saved into Drafts.
Settings → Security and login with Developer mode on, and the new-app dialog with the server URL field highlighted.

Older ChatGPT builds and older help articles call this a connector instead of an app. It's the same thing.

Keep your personal address to yourself. Once it's linked, whoever has it is using your account, the same as with a key. Don't paste it into a shared document. If somebody else might have seen it, ask for unlink_account and add Galley again with a new one. On a Team or Enterprise plan, an address an owner adds for the organization is shared by everyone in it.

Claude Code

The terminal tool. For people who do write code. Any plan that runs Claude Code.

  1. Add the server. --scope user makes it available in every project. Leave it off and it's only added to the current one.
    terminal bash
    claude mcp add --transport http --scope user \
      galley https://mcp.galleyrender.com/mcp
  2. Check it: claude mcp list should show galley connected.
  3. Ask for a render. The first call with no key mints a trial and hands the token back in the response.
  4. Use your key. Add it as a header and every call is on your account rather than on the keyless trial. Re-run the add command with --header; it replaces the entry. Then ask Claude to run whoami — it should say authenticated_via: "header".
    terminal bash
    claude mcp add --transport http galley https://mcp.galleyrender.com/mcp \
      --header "Authorization: Bearer $GALLEY_API_KEY"
A drawing of a terminal running the claude mcp add command, with the URL highlighted. terminal $ claude mcp add --transport http \ --scope user galley \ https://mcp.galleyrender.com/mcp Added HTTP MCP server galley $ claude mcp list galley: https://mcp.galleyrender.com/mcp — connected
One command in a terminal.

Cursor

The editor. Any plan.

  1. Put this in ~/.cursor/mcp.json for every project, or .cursor/mcp.json in a repo for just that one.
    ~/.cursor/mcp.json json
    {
      "mcpServers": {
        "galley": {
          "url": "https://mcp.galleyrender.com/mcp"
        }
      }
    }
  2. Open the Customize panel in the sidebar and make sure galley is toggled on. A server that's toggled off doesn't load or appear in chat.
  3. Ask for a render. The first call with no key mints a trial and hands the token back in the response.
  4. Use your key. Add a headers entry beside the url and every call is on your account rather than on the keyless trial. Reload the server from the Customize panel, then ask for whoami — it should say authenticated_via: "header".
    ~/.cursor/mcp.json json
    {
      "mcpServers": {
        "galley": {
          "url": "https://mcp.galleyrender.com/mcp",
          "headers": { "Authorization": "Bearer glr_sk_…" }
        }
      }
    }
A drawing of an mcp.json file with the Galley server URL highlighted. ~/.cursor/mcp.json { "mcpServers": { "galley": { "url": "https://mcp.galleyrender.com/mcp" } } }
The mcp.json entry, with the URL highlighted.

One call, both halves

Or call the endpoint yourself.

It's an ordinary REST API with a bearer token. MCP and REST use the same renderer, cache and template store, so a template your agent made in an MCP session is the one your production code renders.

you send
POST /v1/render

{
  "template": "invoice@1",
  "format":   "pdf",
  "data":     { … }
}
you get back
200 OK

{
  "id":             "rnd_7hq2m4x8k1bv",
  "status":         "succeeded",
  "url":            "https://…?signature=…",
  "page_count":     1,
  "billable_units": 1,
  "cached":         false
}
Send it again unchanged and cached is true. You get the same file back, with no render and no charge.
curl shell
curl -sS https://api.galleyrender.com/v1/render \
  -H "Authorization: Bearer $GALLEY_API_KEY" \
  -H 'content-type: application/json' \
  -d '{
    "template": "invoice@1",
    "format": "pdf",
    "data": {
      "invoice_number": "INV-1042",
      "issued_on": "2026-09-16",
      "seller": { "name": "Galley Render" },
      "buyer":  { "name": "Acme Robotics" },
      "line_items": [
        { "description": "Starter plan, September", "quantity": 1, "unit_price": 19 }
      ]
    }
  }'

Small jobs finish inside the request and come back 200 with a URL. See POST /v1/render.

Node javascript
// Node 22+. No dependencies.
const res = await fetch("https://api.galleyrender.com/v1/render", {
  method: "POST",
  headers: {
    authorization: `Bearer ${process.env.GALLEY_API_KEY}`,
    "content-type": "application/json",
  },
  body: JSON.stringify({
    template: "invoice@1",
    format: "pdf",
    data: {
      invoice_number: "INV-1042",
      issued_on: "2026-09-16",
      seller: { name: "Galley Render" },
      buyer: { name: "Acme Robotics" },
      line_items: [{ description: "Starter plan", quantity: 1, unit_price: 19 }],
    },
  }),
});

const render = await res.json();
if (!res.ok) throw new Error(render.error.message);

console.log(render.url);        // signed, expires in an hour
console.log(render.page_count); // 1
console.log(render.cached);     // false the first time, true after

You don't need an SDK, but there is one: galley-render on npm and PyPI.

Python python
# Python 3.9+. Standard library only.
import json, os, urllib.request

body = json.dumps({
    "template": "invoice@1",
    "format": "pdf",
    "data": {
        "invoice_number": "INV-1042",
        "issued_on": "2026-09-16",
        "seller": {"name": "Galley Render"},
        "buyer": {"name": "Acme Robotics"},
        "line_items": [{"description": "Starter plan", "quantity": 1, "unit_price": 19}],
    },
}).encode()

req = urllib.request.Request(
    "https://api.galleyrender.com/v1/render",
    data=body,
    headers={
        "Authorization": f"Bearer {os.environ['GALLEY_API_KEY']}",
        "Content-Type": "application/json",
    },
)

render = json.load(urllib.request.urlopen(req))
print(render["url"], render["page_count"], render["cached"])

Swap urllib for requests or httpx if you already have one.

Get a key

There is no signup form.

Not for you, anyway. An account is a tool call, and your agent can make it without leaving the session it's in.

  1. Start without one

    The first 10 PDF pages or 10 images don't need a key. Point a client at the MCP address above and call render. A trial account is minted on the spot, and its token comes back in the response.

  2. Ask for the key when you need it

    Call create_account with an email address. We email a link, and the call returns a four-character request code. The link opens a page that shows who asked and the same code. If it matches, confirm it, call create_account again with the same address, and the API key comes back once. It can't be recovered. We only store a hash of it, so neither the dashboard nor support can read it back to you. Send a random X-Galley-Client-Id on both calls; without one, your client has no identity of its own and the key is shown on the page instead.

    Over MCP, no key requiredshell
    curl -sS https://mcp.galleyrender.com/mcp \
      -H 'content-type: application/json' \
      -H 'accept: application/json, text/event-stream' \
      -d '{
        "jsonrpc": "2.0",
        "id": 1,
        "method": "tools/call",
        "params": {
          "name": "create_account",
          "arguments": { "email": "you@example.com" }
        }
      }'
  3. Keep everything you already made

    When you confirm the request, the trial account of the client that asked is upgraded in place. Templates you created and renders you made during the trial stay on the account, and the free tier of 5 PDF pages or 5 images a month starts from there. Each email address gets one account, so you+galley@example.com reaches the account you@example.com already has. If the address already has an account, we email a one-time code for link_account instead, and nobody has to paste a key into a chat.