Docs

Make pictures, songs, voices and clips from an AI agent or your own code. Same account, same daily moons.

Getting started

  1. Make a key. Open API keys, name it and copy it. It starts with phk_ and is shown once.
  2. Connect. Add the MCP server to your agent, or call the REST API from a script.
  3. Make something. You get an id at once. Check it until it is done, then use its url.

Keys

Send your key in every request as Authorization: Bearer phk_…. A key acts as you: what it makes is in your account, and it uses your daily moons. You can have up to 5 keys; revoke one on the API keys page and it stops working at once. Keep keys out of shared code and screenshots.

MCP server

For AI agents such as Claude, ChatGPT, Claude Code, Cursor or any client that speaks MCP (Streamable HTTP). Apps that support sign-in (claude.ai, ChatGPT) connect with just the address: they open a Phantasia page where you sign in and press Allow, and get their own key, which you can revoke on API keys. Others take a key in a header. Step-by-step for each app: Integrations.

Server address

https://phantasia.pics/api/mcp

Claude Code

claude mcp add --transport http phantasia https://phantasia.pics/api/mcp --header "Authorization: Bearer phk_…"

Cursor and other clients (mcp.json)

{
  "mcpServers": {
    "phantasia": {
      "url": "https://phantasia.pics/api/mcp",
      "headers": {
        "Authorization": "Bearer phk_…"
      }
    }
  }
}

Tools

ToolTakes
make_pictureprompt, shape (square · portrait · landscape)
make_songprompt, seconds (10–60), lyrics (leave out for an instrumental)
make_voicetext, voice, speed (0.8–1.3)
make_clipprompt, shape, length (short 2 s · long 4 s), sound (none · music · narration), sound_text
check_creationid → status and file url when done
list_creationskind, limit (1–50) → your recent creations
list_modelskind → the open models, and which are on
todays_moonswhat you have left today
share_creationid, shared → put it in (or take it out of) the public gallery

Then just ask your agent, for example: “make a picture of a lighthouse at dawn and tell me when it’s ready”.

REST API

Base address https://phantasia.pics/api/v1. JSON in, JSON out. The whole API as an OpenAPI file, for n8n, Make, Postman, GPT Actions or code generators: openapi.json. A map for AI tools: llms.txt.

RequestDoes
POST /creationsStart a creation: {kind, prompt, options} → 202 with its id
GET /creations/{id}One creation: status, and its file url when done
GET /creations?kind=&limit=Your recent creations, newest first (limit 1–50, default 20)
POST /creations/{id}/sharePut it in the public gallery; {"shared": false} takes it out
GET /usageWhat you have used and have left today, per kind
GET /modelsThe open models and which are on (no key needed)
GET /webhooks/secretYour webhook signing secret

Start a song

curl -X POST https://phantasia.pics/api/v1/creations \
  -H "Authorization: Bearer phk_…" -H "Content-Type: application/json" \
  -d '{"kind": "music", "prompt": "lo-fi hip hop, soft piano, rainy evening", "options": {"seconds": 30}}'
{ "id": "6f1c…", "kind": "music", "status": "queued", "wait": "about a minute" }

Check it

curl https://phantasia.pics/api/v1/creations/6f1c… -H "Authorization: Bearer phk_…"
{ "id": "6f1c…", "kind": "music", "status": "done", "prompt": "lo-fi hip hop…",
  "url": "https://…/song.mp3", "error": null, "shared": false,
  "created_at": "2026-11-14T10:02:11Z", "finished_at": "2026-11-14T10:03:05Z" }

Status goes queued → running → done (or failed with an error). Check every few seconds; pictures and voices take seconds, songs about a minute, clips 1–3 minutes.

Webhooks

Instead of checking, add "webhook": "https://…" to POST /creations (or send the header X-Webhook-URL). When the creation finishes we POST this to it:

{ "type": "creation.done",
  "creation": { "id": "6f1c…", "kind": "music", "status": "done", "prompt": "lo-fi hip hop…",
                "url": "https://…/song.mp3", "error": null, "created_at": "…", "finished_at": "…" } }

Failed ones come as creation.failed with an error. The address must be https on a public host name. We try once with a 5 second timeout, so keep checking as a backup for anything important.

Check it came from us

Each message has the headers webhook-id, webhook-timestamp and webhook-signature (the Standard Webhooks scheme). Take your secret from API keys (or GET /webhooks/secret), drop the whsec_ start and base64-decode the rest. HMAC-SHA256 the text id.timestamp.body (the raw body) with it, base64 the result, and compare with the part after v1,:

import base64, hashlib, hmac

def verify(secret, headers, raw_body):
    key = base64.b64decode(secret.removeprefix("whsec_"))
    signed = f"{headers['webhook-id']}.{headers['webhook-timestamp']}.{raw_body}".encode()
    expected = base64.b64encode(hmac.new(key, signed, hashlib.sha256).digest()).decode()
    return any(hmac.compare_digest(expected, s.split(",", 1)[1]) for s in headers["webhook-signature"].split())

Also reject messages whose timestamp is more than 5 minutes old.

Options per kind

All options are optional. In the REST API they go in options; MCP tools take them as named above.

KindOptionsValues
imageaspectsquare · portrait · landscape
musicseconds · lyrics · instrumental10–60 (default 30) · text up to 1000 · true / false
voicevoice · speedaf_heart · af_bella · am_michael · am_fenrir · bf_emma · bm_george · tk_mms · 0.8–1.3
videoaspect · length · sound · sound_prompt / narrationlandscape · portrait · square · short · long · none · music · narration · text up to 200

Prompts are 3–500 characters. See every model on the Models page.

Limits and errors

Your daily moons are shared between the studio, agents and scripts, and refill every day: each day’s first of every kind is free. After that a creation uses credits (every account starts with 100; prices on Pricing), and Pro models always do. GET /usage includes your credits. A creation that fails gives its credits back. Your account needs to be approved before it can make things.

CodeMeans
401Missing, wrong or revoked key
400Something in the request is off; error says what
404No creation with that id in your account
429Today’s free one is used and there aren’t enough credits, or the account is not approved yet

Every error is JSON: {"error": "…"}, written for people.