Docs

Start here

Somewhere to PUT a file for an address

POST/v1/uploadson https://api.auray.ai/v1

Returns a signed URL to upload to and where the file goes afterwards.

Returns a signed URL to upload to and where the file goes afterwards. Bytes never come through this API: the platform's request limit is smaller than the files 3D takes, and two orders of magnitude smaller than the meshes a catalogue model takes.

address names the model the file is for, and everything else follows from it: the bucket, the kinds of file its fields take (an image model takes no audio), the ceiling (max_bytes), and the scope — any scope that may submit to that address. send_as lists every field of the address this file can go in, each saying whether to send the path this call answers (our 3D, auray-ai/hunyuan-3d/image-to-3d, and our video, auray-ai/minimax-h3/text-to-video, neither of which fetches a URL) or the signed url that GET /uploads/{path} answers once the bytes are there (every catalogue model's _url field). An address none of whose fields takes a file is refused with upload_not_accepted.

product is the older spelling and still answers every key it answered, with a Deprecation header and a Link to the guide; send address instead. For the catalogue models' bucket, external, it also takes a kind's scope, for the kinds of file some address of that kind takes — an image with any kind's scope, a mesh with 3d:write — so a key minted today still uploads through a client that can name only the product; a file of a kind outside them is refused 403 insufficient_scope, naming the scopes that would reach it. video is no longer offered — a picture for our video goes through its address, which stores it under a name of its own (api-<idempotency_key>) and reads back only those — and answers only a key that used it: one holding auray-ai-video:write, or video:write beside external:write, never the video kind alone. The upload itself is deleted three days after it landed.

#Authentication

Authorization: Bearer auray_sk_… — A scope that may submit to the address the file is for — the kind it makes (3d:write, image:write, …) or the older product name. product is deprecated: threed takes 3d:write or threed:write, external takes external:write or a kind's scope for the kinds of file that kind's addresses take.

#Request

#Body

application/json, required.

FieldTypeRequiredDefaultDescription
addressstringno—The model the file is for, as its address (hitem3d/hi3d/split) or its slug. Send this or product.
productstringno—Deprecated: name the address instead. Which bucket: threed takes 3d:write or threed:write; external takes external:write for any file, or a kind's scope for the kinds of file some address of that kind takes. One of "threed", "external".
content_typestringyes—The file's type. An address accepts the kinds its fields take; the refusal lists the types. One of "application/json", "application/octet-stream", "application/pdf", "application/zip", "audio/flac", "audio/mp4", "audio/mpeg", "audio/ogg", "audio/wav", "audio/x-wav", "image/jpeg", "image/png", "image/webp", "model/gltf+json", "model/gltf-binary", "model/obj", "model/stl", "text/plain", "video/mp4", "video/quicktime", "video/webm".
idempotency_keystringyes—Required here, unlike on a submit. It names the object, so a retry lands on the same one instead of failing as already-existing.

#Example request

curl -X POST https://api.auray.ai/v1/uploads \
  -H "Authorization: Bearer $AURAY_API_KEY" \
  -H "content-type: application/json" \
  -d '{
  "address": "auray-ai/hunyuan-3d/image-to-3d",
  "content_type": "image/png",
  "idempotency_key": "0b6f3c52-8f7e-4c1d-9a3b-5e2f1d7c4a90"
}'

#Responses

#200 OK

OK

FieldTypeRequiredDefaultDescription
objectstringno—One of "upload".
upload_urlstringno—PUT the bytes here with Content-Type set to what you declared. Send no Auray credential: the signature in the URL is the authorisation. A URL.
pathstringno—The object's key, <your account id>/<idempotency_key>.<ext> — <your account id>/api-<idempotency_key>.<ext> for our video's address. Where send_as says path, send it as it is.
bucketstringno—
addressstringno—The address's published path, when the request named one.
max_bytesintegerno—The bucket's ceiling; a larger PUT is refused by storage.
expires_atstringno—Two hours. Supabase fixes this and exposes no option.
readstringno—Where to ask for the read URL once the bytes are there: GET /uploads/{path}?address=… (or ?product=… for the deprecated spelling), absolute. Use this address as given. A URL.
send_asobject[]no—Every field of the address this file can go in. Null when only the deprecated product named an unknown address. May be null.
send_as[].fieldstringyes—The field's name, as GET /models/{address} lists it.
send_as[].kindstringyes—One of "image", "video", "audio", "file", "mesh".
send_as[].listbooleanyes—True when the field takes a list, of which this file is one element.
send_as[].valuestringyes—path: send the path above. read_url: send the url that read answers. One of "path", "read_url".

Headers:

HeaderDescription
RateLimit-LimitBurst capacity.
RateLimit-Remaining
RateLimit-ResetSeconds until refill.
X-RateLimit-Limit
X-RateLimit-Remaining
X-RateLimit-ResetUnix seconds. The X- spelling is what client libraries read.
X-Request-Id
DeprecationPresent only when the request named product rather than address: the day that spelling was deprecated, as @<unix seconds> (RFC 9745).
LinkBeside Deprecation: the guide section naming the replacement, rel="deprecation".

#400 Bad Request

#401 Unauthorized

#403 Forbidden

#413 Payload Too Large

#429 Too Many Requests

#503 Service Unavailable

#Status codes

StatusMeaning
200OK
400Bad Request
401Unauthorized
403Forbidden
413Payload Too Large
429Too Many Requests
503Service Unavailable

Every refusal names its code, and every code is explained at Errors.

Start typing to search every page of the documentation.

Up arrow Down arrow to moveEnter to openEscape to close