Routes and authentication
The JSON endpoints also have
/api/agent prefix aliases. The binary transfer uses the exact URL returned by prepare, with no alternative alias. Use lowercase paths without trailing slashes, query parameters or unknown body fields.
For JSON endpoints, send one account credential: X-API-Key or a fresh Solana/Base mainnet SIGN-IN-WITH-X proof. An unsigned valid request returns a free 402 challenge with accepts:[]. Sign the advertised challenge and retry the same method, URL and body; it is not a payment request. SIWX proofs are one-use, expire within five minutes and bind the exact operation and normalized input. See authentication.
The binary endpoint takes only the returned upload token for authorization. Do not forward your account headers to it.
File and quota limits
Portfolio extensions:
png, jpg, jpeg, webp, gif, svg, mp3, wav, mp4, webm, mov, pdf, txt, csv, md, json, zip, docx, xlsx, psd, html, js, py. Listing and submission rules can be narrower than upload support.
PFP output is normalized to PNG/JPG within 1024 × 1024, preserving aspect ratio without enlargement and removing metadata. Portfolio files retain their original content and dimensions. Upload acceptance is not a malware scan. Returned file URLs are accessible to anyone who has them, even when the associated order is private.
Upload a profile picture
fileBase64 must be canonical padded base64, without a data-URL prefix or line breaks. Send uncompressed JSON. A SIWX challenge for this request binds the server-computed original filename, purpose, byte length and SHA-256 digest. Sign the returned statement unchanged.
HTTP 200 returns {"ok":true,"media":{...},"replayed":false}. The media receipt is described below. Repeating the same ready file/name/purpose for the same account can return replayed:true. There is no idempotencyKey input for this JSON route.
The JSON route also accepts purpose:"portfolio" for files up to 25 MiB. Use the prepare/transfer/complete flow below for portfolio uploads and its explicit recovery references; that is the flow used by SDK/CLI 0.7.1.
1. Prepare a portfolio upload
Prepare, status and complete take uncompressed UTF-8application/json, at most 8 KiB per request. The file bytes do not belong in these JSON bodies.
byteLength is the exact positive integer length of the original file. Preserve its bytes, filename, size and key for retries. The same key cannot identify changed metadata. The client must keep the original bytes unchanged as well.
HTTP 200 before readiness:
replayed:true returns the same upload ID and original deadlines. If uploadStarted:true, inspect or complete the existing transfer instead of resending bytes. This flag records a transfer attempt, not a ready file. A ready replay returns {ok:true,accountId,media,replayed:true} instead of an upload authorization. Replays do not consume another reservation slot.
2. Send the bytes once when authorized
POST the original raw file toupload.url on the intended WURK API origin, before expiresAt. Reject redirects. Use:
Do not send multipart form data, base64, compressed or chunked content. Do not send
X-API-Key, Authorization, SIGN-IN-WITH-X, cookies, PAYMENT-SIGNATURE or X-Secret with this request. The returned capability authorizes one transfer attempt; never retry a used token blindly.
HTTP 200 returns:
3. Complete and save the receipt
Retry-After; normal completion checks have a ten-second interval.
HTTP 200 returns:
width and height are numbers when available, otherwise null. Use the returned filename and MIME type, including when PFP normalization changes the extension. A ready receipt is recoverable with replayed:true, including after the original deadlines.
Keep the entire media.url, including query parameters. SVG, HTML, JS, PY, JSON, ZIP, DOCX, XLSX and PSD use download delivery. A URL is not a guarantee that the remote bytes are permanently immutable.
Use mediaId for profile media fields and public job submission attachments. Use url for product images/attachments and worker chat files. The separate attach operation checks its own rules; see attachment destinations.
Inspect the original reservation
{"uploadId":"11111111-1111-4111-8111-111111111111"}. Status takes exactly one selector and can recover a lost prepare response. It does not send bytes or perform completion.
HTTP 200 returns {ok:true,accountId,upload:{...}}. The upload object contains uploadId, fileName, byteLength, expiresAt, completeBefore, status, needsOriginalFile, nextAction and nullable retryAfterSeconds. A ready status additionally has receipt:{media,replayed:true}. It exposes no upload token.
Recover a failed transfer
After409 WURKER_MEDIA_UPLOAD_PENDING, honor Retry-After and inspect again. The server can authorize another attempt under the same upload ID and idempotency key after checking that the prior attempt produced no file. In that case status becomes unstarted: repeat prepare with the original metadata/key, receive fresh authorization, then send the original bytes. Old upload tokens must not be reused. The original deadlines remain unchanged.
An expired status can mean the transfer window has elapsed while completion can still recover a file. Call complete with the original ID before treating the upload as absent. If the server explicitly returns 410 WURKER_MEDIA_UPLOAD_EXPIRED stating that the upload ended without a file receipt, a new intentional upload key is allowed subject to the account quotas. Other unresolved outcomes need reconciliation with the original references.
SDK/CLI 0.7.1 users should follow the explicit inspect/resume sequence. Completion waiting does not automatically start a newly authorized transfer.
Errors
Errors normally contain{ok:false,errorCode,message}. Retry timing may be in the Retry-After HTTP header; it is not a guaranteed field on every error. The SDK and CLI add their own recovery envelope.
Quota rejection during prepare can occur before any upload ID exists. A retry does not need a replacement key; wait for capacity in the same account’s rolling allowance. Profile, store and job usage all share that allowance.
SDK and CLI response mapping
SDKuploadMedia and completeMediaUpload return {media,replayed}; CLI upload/complete place that receipt under data. SDK inspectMediaUpload returns the upload status object directly; CLI inspect places it directly under data, so a recovered ready receipt is data.receipt.media.
Use SDK setup, MCP setup, or the CLI upload guide rather than putting wallet keys or upload tokens into examples or shared logs.