Neurall

Files

API

Upload the references a generation works from. Returns file ids you pass in a feature's args. There is no delete here and nothing to clean up: an upload is removed 24 hours after it lands, whether or not a generation used it. To remove a finished result early, delete the generation.

Generated from the live contract. The same page as markdown, and the whole surface as openapi.json.

Upload Files

POST /v3/files/upload

Turns a local file into a file id: `args` fields like `image` and `audio` take these ids, never a URL or base64. Call this first, then generate.

Send `multipart/form-data` with a `files` field: up to 10 files, 30 MB each; images, audio, video or documents (PDF, ZIP, text, JSON). Uploading is permissive on purpose; each feature checks its own limits at generation time, so see that feature's docs for what it accepts.

An upload is deleted 24 hours after it lands. Keep the original on your side and upload it again when you need it later.

curl -X POST "https://api.neurall.io/v3/files/upload" \
  -H "Authorization: nl-YOUR_API_KEY" \
  -F "files=@/path/to/file"

Body

filesfile[]required
One or more files, all under the same `files` field name. Up to 10 per request, 30 MB each; over either limit answers 400.

Responses

200 The uploaded file ids

filesobject[]
One entry per uploaded file, in submission order.
files[].idstring
The file id to pass in a feature's args.
files[].originalNamestring
files[].sizeinteger
Bytes received.
Example
{
  "files": [
    {
      "id": "f_9c21e0b4",
      "originalName": "portrait.jpg",
      "size": 734112
    }
  ]
}

401 Unauthorized

Create an Upload URL

POST /v3/files/upload-url

Both routes produce the same file id; this one is for callers the multipart route shuts out. The id comes first and the bytes go second, straight to storage, over a signed URL that carries its own credential for 15 minutes. No Authorization header travels with the bytes, so an agent on MCP (whose key lives in its client config, not its shell) or a client you would rather not hand your key can finish the upload. If you can POST the file, use /v3/files/upload instead.

The multipart route's limits apply (30 MB, same accepted types). Both echoed headers are signed: a PUT that changes or omits either is refused by storage with a 403.

The id means nothing until the PUT answers 200, then behaves exactly like a multipart id, 24 hour expiry included.

1Ask for the URL

POST the file's exact size in bytes and its media type. Nothing is uploaded yet; you are reserving an id and getting a signed address for the bytes.

curl -X POST "https://api.neurall.io/v3/files/upload-url" \
  -H "Authorization: nl-YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "bytes": 734112, "contentType": "image/jpeg" }'
It answers
{
  "file": {
    "id": "f_51ac9d20"
  },
  "uploadUrl": "https://s3.amazonaws.com/neurall-uploads/f_51ac9d20?X-Amz-Signature=…",
  "method": "PUT",
  "headers": {
    "Content-Type": "image/jpeg",
    "Content-Length": "734112"
  },
  "expiresIn": 900
}

2PUT the bytes to uploadUrl

Send the file to `uploadUrl` with exactly the echoed headers and no Authorization header (curl derives Content-Length from the file itself). A 200 with an empty body means the id is live. The URL is good for 15 minutes; past that, start over at step 1.

curl -X PUT "https://s3.amazonaws.com/neurall-uploads/f_51ac9d20?X-Amz-Signature=…" \
  -H "Content-Type: image/jpeg" \
  --data-binary @portrait.jpg

3Use the id like any upload

The id now behaves exactly like one from POST /v3/files/upload: pass it in a feature's args, and it expires 24 hours after it landed.

curl -X POST "https://api.neurall.io/v3/generations" \
  -H "Authorization: nl-YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "feature": "image/self-portrait",
    "args": {
      "image": "f_51ac9d20",
      "prompt": "Cinematic studio portrait, moody side light."
    }
  }'
It answers
{
  "generation": {
    "id": "9f2c1d84-6a3b-4f21-9c77-2f0a1b8e5d43",
    "feature": "image/self-portrait",
    "kind": "image",
    "status": "pending"
  }
}

Body

bytesintegerrequired
The file's exact size in bytes. Signed into the URL, so the upload must send precisely this many. Over 30 MB answers 413.
contentTypestringrequired
The file's media type, e.g. `image/jpeg`. Also signed, and stored on the object: promoting an upload into a saved subject or product copies it onto the permanent asset. Must be an accepted type (image, audio, video, or PDF/ZIP/text/JSON documents) or the request answers 415.

Responses

200 The file id and where to PUT the bytes

fileobject
file.idstring
uploadUrlstring
methodstring
headersobject
The exact headers the PUT must carry.
expiresIninteger
Seconds the URL stays valid.
Example
{
  "file": {
    "id": "f_51ac9d20"
  },
  "uploadUrl": "https://s3.amazonaws.com/neurall-uploads/f_51ac9d20?X-Amz-Signature=…",
  "method": "PUT",
  "headers": {
    "Content-Type": "image/jpeg",
    "Content-Length": "734112"
  },
  "expiresIn": 900
}

400 Missing or malformed bytes or contentType

401 Unauthorized

413 The file is over 30 MB