Upload flow

The Aion API uses a single one-shot request: send your file, get a download URL back. This page covers every detail of the upload, status polling, and download-url endpoints.

1. Upload a file

Send a POST to /v1/aion/upload with your file as multipart/form-data:

POST /v1/aion/upload
X-Aion-Key: YOUR_API_KEY
Content-Type: multipart/form-data

[file field: your file]

Response (202)

{
  "uploadId": "up_a1b2c3d4e5f6g7h8",
  "status": "PENDING_UPLOAD",
  "statusUrl": "/v1/upload/up_a1b2c3d4e5f6g7h8/status",
  "downloadUrl": "https://payaion.com/d/abc123xyz789",
  "expiresAt": "2026-02-22T14:35:00.000Z"
}

The downloadUrl is a shareable link. The file is available for download as soon as it is stored on Walrus. Use the status endpoint to confirm the upload is ready.

1b. Upload from a URL

If your bot or webhook receives a temporary file URL (Discord, Slack, automation platforms), you can skip downloading the file yourself. Send the URL and Payaion fetches it for you:

POST /v1/aion/upload-url
X-Aion-Key: YOUR_API_KEY
Content-Type: application/json

{
  "url": "https://cdn.example.com/photo123.jpg",
  "fileName": "photo123.jpg"
}

Response (202)

Same shape as a regular upload — you get uploadId, downloadUrl, and statusUrl.

Rules

  • Only https:// URLs are accepted
  • Private IPs, localhost, and internal hostnames are blocked (SSRF protection)
  • The fetch aborts as soon as the remote file exceeds your plan ceiling (500 MB on Basic, 1 GB on Pro), both on Content-Length and mid-stream
  • Fetch timeout: 30 seconds 408 fetch_timeout. An unreachable or erroring host returns 502 fetch_failed
  • The fileName field is optional — derived from the URL path if omitted

Code examples

curl

curl -X POST https://payaion-api.fly.dev/v1/aion/upload-url \
  -H "X-Aion-Key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"url": "https://cdn.example.com/photo123.jpg"}'

JavaScript

const resp = await fetch('https://payaion-api.fly.dev/v1/aion/upload-url', {
  method: 'POST',
  headers: {
    'X-Aion-Key': API_KEY,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({ url: fileUrl }),
});

const { downloadUrl } = await resp.json();

Python

import requests

resp = requests.post(
    "https://payaion-api.fly.dev/v1/aion/upload-url",
    headers={"X-Aion-Key": API_KEY},
    json={"url": file_url},
)

data = resp.json()
print("Share this URL:", data["downloadUrl"])

Pricing & marketplace headers

Both upload endpoints accept optional headers for pricing and auto-listing:

HeaderDescription
X-Price-Per-DownloadUSD price per download (non-negative number). Omit for free files.
X-Listing-IntentJSON object with title, category, tags. Auto-lists on the marketplace once upload is READY.

See Marketplace API for the full listing endpoint and validation rules.

Retry-safe uploads (Idempotency-Key)

Network failures happen. To safely retry an upload without creating duplicates, include an Idempotency-Key header:

POST /v1/aion/upload
X-Aion-Key: YOUR_API_KEY
Idempotency-Key: my-unique-request-id-abc123
Content-Type: multipart/form-data

[file field: your file]
  • Key must be 8–128 characters, alphanumeric, dashes, underscores, or dots
  • If you retry with the same key, you get the original 202 response back
  • Replayed responses include an X-Idempotent-Replay: 1 header
  • If the original request is still in progress, you get 409 with Retry-After
  • Cached responses expire after 24 hours

Best practice: Always include an Idempotency-Key when your bot or script may retry on failure. Generate a unique key per intended upload (e.g. UUID or hash of the file name + timestamp).

2. Check upload status

After uploading, the file is stored on Walrus and available for download almost immediately. Poll the status endpoint to confirm:

GET /v1/upload/{uploadId}/status
X-Aion-Key: YOUR_API_KEY

Response (200)

{
  "uploadId": "up_a1b2c3d4e5f6g7h8",
  "status": "READY",
  "ready": true,
  "updatedAt": "2026-02-21T14:35:10.000Z"
}

Possible status values:

  • PENDING_UPLOAD — Upload record created, worker handoff pending
  • UPLOADING — Upload worker is pushing file to storage
  • READY — File stored on Walrus network, available for download
  • FAILED — Upload failed
  • DELETING — File is being removed from the storage network
  • DELETED — File removed from both database and Walrus storage network

3. Get a fresh download URL

If you need a new share link (e.g. the original expired), request one:

POST /v1/upload/{uploadId}/download-url
X-Aion-Key: YOUR_API_KEY
Content-Type: application/json

Response (200)

{
  "uploadId": "up_a1b2c3d4e5f6g7h8",
  "downloadUrl": "https://payaion.com/d/abc123xyz789",
  "expiresAt": "2026-02-21T14:37:10.000Z",
  "ready": true
}

4. What recipients see

When someone opens the downloadUrl, they see a download page with:

  • File name and size
  • Expiration date
  • Download button (streams the file directly)

The page is the same design as manual uploads — no "uploaded by agent" badge or difference visible to recipients. The file is downloadable as soon as it is stored on Walrus. If the upload is still in progress, recipients will see a brief loading state that auto-refreshes.

Code examples

curl

curl -X POST https://payaion-api.fly.dev/v1/aion/upload \
  -H "X-Aion-Key: YOUR_API_KEY" \
  -H "Idempotency-Key: $(uuidgen)" \
  -F "file=@document.pdf"

JavaScript

const form = new FormData();
form.append('file', new Blob([fileBuffer]), 'document.pdf');

const resp = await fetch('https://payaion-api.fly.dev/v1/aion/upload', {
  method: 'POST',
  headers: {
    'X-Aion-Key': API_KEY,
    'Idempotency-Key': crypto.randomUUID(),
  },
  body: form,
});

const { uploadId, downloadUrl, expiresAt } = await resp.json();

Python

import uuid, requests

resp = requests.post(
    "https://payaion-api.fly.dev/v1/aion/upload",
    headers={
        "X-Aion-Key": API_KEY,
        "Idempotency-Key": str(uuid.uuid4()),
    },
    files={"file": open("document.pdf", "rb")},
)

data = resp.json()
print("Share this URL:", data["downloadUrl"])

5. Delete an upload

Delete a file before it expires. This removes it from both the database and the Walrus storage network. The share link will stop working immediately.

DELETE /v1/upload/{uploadId}/delete
X-Aion-Key: YOUR_API_KEY

Response (200)

{
  "ok": true,
  "status": "DELETED"
}

If the file was already deleted, the endpoint returns success. If the on-chain cleanup fails, the database record is still removed and a warning field is included in the response.

Limits

  • Max file size: 500 MB on Basic, 1 GB on Pro
  • Rate limit: 5 uploads per key per minute
  • Concurrency: 2 parallel uploads per key
  • File lifetime: 30 days on Basic, while your subscription stays active on Pro

See Limits & Quotas for full details.

Next steps

Need an API key?

  • Create API keys in seconds
  • Dashboard with upload history
  • No passwords
  • No gas fees to sign in
Create account & get API key