Upload
Data item upload endpoints (single and multi-part)
Posts a signed ANS-104 data item OR raw data to Arweave
Two Upload Modes:
1. Traditional ANS-104 Upload (Client-Signed)
Client creates and signs ANS-104 data item before uploading. The service detects ANS-104 format by checking the first 2 bytes (signature type 1-8).
Payment options:
- Traditional balance (user has pre-loaded credits)
- x402 payment (include X-PAYMENT header with EIP-3009 authorization)
2. Raw Data Upload (Server-Signed, x402 Only)
Simplified flow for AI agents and applications without ANS-104 signing capabilities. Requires RAW_DATA_UPLOADS_ENABLED=true on server.
The service automatically detects raw data (non-ANS-104) and:
- Returns 402 Payment Required if no X-PAYMENT header provided
- Creates and signs ANS-104 data item server-side using RAW_DATA_ITEM_JWK wallet
- Adds attribution tags: Bundler, Upload-Type, Payer-Address, Upload-Timestamp, Content-Type
- Validates x402 payment against payer's Ethereum address (not the signer)
- Returns response with both
owner(server wallet) andpayer(Ethereum address) fields
Request formats for raw data:
- Binary upload with X-Tag-* headers for custom tags
- JSON envelope:
{"data": "<base64>", "contentType": "...", "tags": [...]}
When using X-PAYMENT header, Content-Length must be present to enable fraud detection.
Header Parameters
int64value <= 4294967296"application/octet-stream"Base64-encoded x402 payment authorization for pay-as-you-go USDC payments.
x402 Payment Flow:
- Upload without X-PAYMENT → Server returns 402 with payment requirements
- Client creates EIP-3009 authorization and signs with EIP-712
- Client retries upload with X-PAYMENT header containing base64(JSON)
- Server verifies signature, settles USDC transfer, processes upload
- Server returns receipt with x402Payment object and X-Payment-Response header
Decoded Structure (see X402PaymentHeader schema):
{
"x402Version": 1,
"scheme": "exact",
"network": "base",
"payload": {
"signature": "0x...(EIP-712 signature)...",
"authorization": {
"from": "0xPayerAddress",
"to": "0xBundlerPaymentAddress",
"value": "30490",
"validAfter": 0,
"validBefore": 1735689600,
"nonce": "0x...(32 bytes hex)..."
}
}
}Creating the Header:
const payload = { x402Version: 1, scheme: "exact", network: "base", payload: {...} };
const xPayment = Buffer.from(JSON.stringify(payload)).toString("base64");
headers["X-PAYMENT"] = xPayment;Requirements:
- Content-Length header MUST be present when using X-PAYMENT
- Authorization value must match or exceed the 402 response's maxAmountRequired
- Nonce must be unique (32-byte random hex string)
Supported Networks: base, base-sepolia, ethereum-mainnet, polygon-mainnet
Standards:
- x402 Protocol: https://github.com/coinbase/x402
- EIP-3009: TransferWithAuthorization (gasless USDC transfers)
- EIP-712: Typed structured data signing
byteCustom tags for raw data uploads (server-signed mode only). Use pattern X-Tag-Name where Name becomes the tag name. Example: X-Tag-App-Name: MyApp creates tag {"name": "App-Name", "value": "MyApp"}
Auto-added tags for raw uploads:
- Bundler: Service name
- Upload-Type: "raw-data-x402"
- Payer-Address: Ethereum address from X-PAYMENT
- Upload-Timestamp: Unix timestamp
- Content-Type: From Content-Type header
- X402-Tx-Hash: Blockchain transaction hash (if x402 payment)
- X402-Payment-ID: Unique payment identifier UUID (if x402 payment)
- X402-Network: Payment network e.g. "base" (if x402 payment)
A signed ANS-104 data item (binary) OR raw data (binary/JSON envelope for server-signing)
Traditional ANS-104 data item (client-signed)
binaryResponse Body
curl -X POST "https://turbo.ardrive.io/tx" \ -H "content-length: 4294967296" \ -H "content-type: application/octet-stream" \ -H "X-PAYMENT: eyJ4NDAyVmVyc2lvbiI6MSwic2NoZW1lIjoiZXhhY3QiLCJuZXR3b3JrIjoiYmFzZSIsInBheWxvYWQiOnsic2lnbmF0dXJlIjoiMHgxMjM0NTY3ODkwYWJjZGVmLi4uIiwiYXV0aG9yaXphdGlvbiI6eyJmcm9tIjoiMHg3NDJkMzVDYzY2MzRDMDUzMjkyNWEzYjg0NEJjOWU3NTk1ZjBiRWIwIiwidG8iOiIweENGZDNmOTk2NDQ3YTU0MUNiZmJhNTQyMjMxMEVEYjQxN2Q5ZjJjRTYiLCJ2YWx1ZSI6IjMwNDkwIiwidmFsaWRBZnRlciI6MCwidmFsaWRCZWZvcmUiOjE3MzU2ODk2MDAsIm5vbmNlIjoiMHgxMjM0NTY3ODkwYWJjZGVmMTIzNDU2Nzg5MGFiY2RlZjEyMzQ1Njc4OTBhYmNkZWYxMjM0NTY3ODkwYWJjZGVmIn19fQ==" \ -H "X-Tag-*: MyApp" \ -H "Content-Type: application/json" \ -d '{}'Traditional signed ANS-104 data item upload paid via x402 USDC payment. Client signs data item with their wallet, includes X-PAYMENT header with USDC authorization.
{
"id": "QpmY8mZmFEC8RxNsgbxSV6e36OF6quIYaPRKzvUco0o",
"timestamp": 1700590909589,
"version": "0.2.0",
"owner": "0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb0",
"deadlineHeight": 1310000,
"dataCaches": [
"arweave.net"
],
"fastFinalityIndexes": [
"arweave.net"
],
"winc": "1000000",
"signature": "iU_S6uuG1OD8k0XqMGOmbcKfysDckEMUy4R9-ODPXiQj...",
"public": "qREovbmD6oxgHYNCzOeTei07lSz0-YLcjnvgSDzqpts...",
"x402Payment": {
"paymentId": "550e8400-e29b-41d4-a716-446655440000",
"transactionHash": "0xb8a52725cdc816d060392dcecaa1528872acf9ea0bd097a86e2432dd5d2213ca",
"network": "base-mainnet",
"mode": "payg"
}
}"Data Item Exists""Invalid Content Type"{
"x402Version": 1,
"accepts": [
{
"scheme": "exact",
"network": "base-mainnet",
"maxAmountRequired": "1000000",
"resource": "/v1/tx",
"description": "Upload 1024 bytes to Arweave via AR.IO Bundler",
"mimeType": "application/json",
"payTo": "0x6A0A10FFD285c971B841bee8892878c0d583Bf67",
"maxTimeoutSeconds": 300,
"asset": "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
"extra": {
"name": "USD Coin",
"version": "2"
}
}
],
"error": "Insufficient balance"
}Posts a signed data item to arweave for a specific token
Uploads a signed ANS-104 data item to the bundler with token-specific handling. Payment can be handled via:
- Traditional balance (user has pre-loaded credits)
- x402 payment (include X-PAYMENT header with EIP-3009 authorization)
When using X-PAYMENT header, Content-Length must be present to enable fraud detection.
Path Parameters
The token to use for validating the transaction.
"arweave" | "ethereum" | "solana"Header Parameters
int64value <= 4294967296"application/octet-stream"Base64-encoded x402 payment authorization for pay-as-you-go USDC payments.
x402 Payment Flow:
- Upload without X-PAYMENT → Server returns 402 with payment requirements
- Client creates EIP-3009 authorization and signs with EIP-712
- Client retries upload with X-PAYMENT header containing base64(JSON)
- Server verifies signature, settles USDC transfer, processes upload
- Server returns receipt with x402Payment object and X-Payment-Response header
Decoded Structure (see X402PaymentHeader schema):
{
"x402Version": 1,
"scheme": "exact",
"network": "base",
"payload": {
"signature": "0x...(EIP-712 signature)...",
"authorization": {
"from": "0xPayerAddress",
"to": "0xBundlerPaymentAddress",
"value": "30490",
"validAfter": 0,
"validBefore": 1735689600,
"nonce": "0x...(32 bytes hex)..."
}
}
}Creating the Header:
const payload = { x402Version: 1, scheme: "exact", network: "base", payload: {...} };
const xPayment = Buffer.from(JSON.stringify(payload)).toString("base64");
headers["X-PAYMENT"] = xPayment;Requirements:
- Content-Length header MUST be present when using X-PAYMENT
- Authorization value must match or exceed the 402 response's maxAmountRequired
- Nonce must be unique (32-byte random hex string)
Supported Networks: base, base-sepolia, ethereum-mainnet, polygon-mainnet
Standards:
- x402 Protocol: https://github.com/coinbase/x402
- EIP-3009: TransferWithAuthorization (gasless USDC transfers)
- EIP-712: Typed structured data signing
byteA signed data item
binaryResponse Body
curl -X POST "https://turbo.ardrive.io/tx/:token" \ -H "content-length: 4294967296" \ -H "content-type: application/octet-stream" \ -H "X-PAYMENT: eyJ4NDAyVmVyc2lvbiI6MSwic2NoZW1lIjoiZXhhY3QiLCJuZXR3b3JrIjoiYmFzZSIsInBheWxvYWQiOnsic2lnbmF0dXJlIjoiMHgxMjM0NTY3ODkwYWJjZGVmLi4uIiwiYXV0aG9yaXphdGlvbiI6eyJmcm9tIjoiMHg3NDJkMzVDYzY2MzRDMDUzMjkyNWEzYjg0NEJjOWU3NTk1ZjBiRWIwIiwidG8iOiIweENGZDNmOTk2NDQ3YTU0MUNiZmJhNTQyMjMxMEVEYjQxN2Q5ZjJjRTYiLCJ2YWx1ZSI6IjMwNDkwIiwidmFsaWRBZnRlciI6MCwidmFsaWRCZWZvcmUiOjE3MzU2ODk2MDAsIm5vbmNlIjoiMHgxMjM0NTY3ODkwYWJjZGVmMTIzNDU2Nzg5MGFiY2RlZjEyMzQ1Njc4OTBhYmNkZWYxMjM0NTY3ODkwYWJjZGVmIn19fQ==" \ -H "Content-Type: application/octet-stream" \ -d 'string'{
"id": "QpmY8mZmFEC8RxNsgbxSV6e36OF6quIYaPRKzvUco0o",
"owner": "8wgRDgvYOrtSaWEIV21g0lTuWDUnTu4_iYj4hmA7PI0",
"payer": "0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb0",
"dataCaches": [
"arweave.net"
],
"fastFinalityIndexes": [
"arweave.net"
],
"deadlineHeight": 1310000,
"timestamp": 1700590909589,
"version": "0.1.0",
"signature": "iU_S6uuG1OD8k0XqMGOmbcKfysDckEMUy4R9-ODPXiQjKXuT4lTngRFBFKO5NQT1iIfqSDKcbTRL6gJowM_L7bBQZRGkojzXD0PNNU2F0bNNJ80VtUktHifGTXbCgz5kiFciL19n0P3nX6ZfXnOn-H8ALZzRJV69apdvwqitpNKxLMPyc-QA0QBxmC3CKPz_7fy2Qg0QHr5g_ZT2Of-YJ_RsZTEoc3g1fgzsEmMBPOsx4XtPrhV6llnA3pncngzHbPdFvypdWiO8Bvr0EWmazNsoanuwK5uKJ_ROIGXW_dBBGN8Vrfv5U6dJnhJVn5IE7JFlixpFTluF_ICRzbUq2pk_re6jEtW1H3ItH2iN0UeFUw1uDbq3HJW6lDc8aOwDwDspJI11KEI6uCz5QmQy2V8DvRknoqcxmuihF6XmmJIZgTVeo6LNufEis9kFxqtc3Dh_gn8z0cDXKEKFycudckmcHP7vkWD68uSssMMJIdVgwvPZss06svfRnI-E33j3MrQI9FzMIv-7Df8iYATyeyldM1v3gexG0kQm0AMG1_8_SLqwu2QlqzM41mrK5vNmQxOVdIQSOPWPvzbF-YGRwpCjlveRBuARGC9JNC4UipvDYri2gRWuBx2uDL7dmVFv1gRll3dYNMYaMULHYngtrrCynB3Cyfhh7cyPlwuNlk0",
"public": "qREovbmD6oxgHYNCzOeTei07lSz0-YLcjnvgSDzqptsCiqNOtB3RKUToSX5hkPD45fJDY4057XkkcsQRuGsU8y9rgm37i-Kiyd5Z_iy6pJXrwi9XnAgGL118lIV790GZ7xe5o3DvPV3Px74C0ABsfL9lW86D4t_qClJ6wSQksKNd7rnUImIvHW0vxLswST7dfUngevzKt4kv48VTub4951XdUHjb45Uurf7xFYSCizAGtGqr5GYDFrVk-mNrzFH5bXt06PJfxe9E5ujIE5Uq1Az6vqEOO0E1mWmXqdTPluAxcjmgktkoNLHZnnU-BsYuFaTWW5NU3aS-RgJKXYs9O6Dc1-2SITl-H_wtdGNSj31fj72UkaAbkpA1mionK-8bOIkSpYgKCyTC42oHh1Fw4SLXPyLxBj1w6F32LSLjpse5dmKymj4fJPezCMdi709uIiVT7XOm4LZBCzFOxS6-UNxgE57dBFpcWkcqNO3p00biYqH5d5bzvK3bwd-4j0KyKlqPYTProSnd3P6ROZuuJaLoLD_Or5-L_dUjawHz-DFlzmckYaf8l3XdzPM4JSsE8CEDZl0NCN0AidXt_wjbr6k9JsO7cnLB226AjxhcyuxZOlhgkIn7EbxpVXx-O2mXkcXF8PixCP0k5brtriLeF1MAdspgd_S-LKQeGVr7-mk",
"x402Payment": {
"paymentId": "550e8400-e29b-41d4-a716-446655440000",
"transactionHash": "0xb8a52725cdc816d060392dcecaa1528872acf9ea0bd097a86e2432dd5d2213ca",
"network": "base-mainnet",
"mode": "payg"
}
}"Data Item Exists""Insufficient balance"Posts a signed ANS-104 data item with x402 payment
x402-Specific Upload Endpoint for Signed Data Items
This endpoint is identical to /tx but explicitly signals x402 payment intent. It accepts
signed ANS-104 data items and processes x402 USDC payments.
Use Case: Clients that want to use x402 payments for signed data item uploads.
Payment Flow:
- Client uploads without X-PAYMENT header → Server returns 402 Payment Required
- Server response includes x402 payment requirements (USDC amount, recipient, network)
- Client creates EIP-3009 authorization and signs with EIP-712
- Client retries upload with X-PAYMENT header (base64-encoded JSON)
- Server verifies payment signature, settles USDC transfer on-chain
- Server accepts upload and returns receipt with x402Payment object
Requirements:
- Content-Type: application/octet-stream
- Content-Length: Required (for fraud detection with x402 payments)
- X-PAYMENT: Required for x402 payment authorization (after 402 response)
Supported Networks:
- base (Base mainnet) - Default, enabled
- base-sepolia (Base Sepolia testnet) - Requires X402_BASE_TESTNET_ENABLED=true
- ethereum-mainnet - Requires X402_ETH_ENABLED=true
- polygon-mainnet - Requires X402_POLYGON_ENABLED=true
Note: This endpoint has the same behavior as /tx - the separate path is for clarity
and allows clients to explicitly signal x402 payment intent.
Header Parameters
int64value <= 4294967296"application/octet-stream"Base64-encoded x402 payment authorization for pay-as-you-go USDC payments.
x402 Payment Flow:
- Upload without X-PAYMENT → Server returns 402 with payment requirements
- Client creates EIP-3009 authorization and signs with EIP-712
- Client retries upload with X-PAYMENT header containing base64(JSON)
- Server verifies signature, settles USDC transfer, processes upload
- Server returns receipt with x402Payment object and X-Payment-Response header
Decoded Structure (see X402PaymentHeader schema):
{
"x402Version": 1,
"scheme": "exact",
"network": "base",
"payload": {
"signature": "0x...(EIP-712 signature)...",
"authorization": {
"from": "0xPayerAddress",
"to": "0xBundlerPaymentAddress",
"value": "30490",
"validAfter": 0,
"validBefore": 1735689600,
"nonce": "0x...(32 bytes hex)..."
}
}
}Creating the Header:
const payload = { x402Version: 1, scheme: "exact", network: "base", payload: {...} };
const xPayment = Buffer.from(JSON.stringify(payload)).toString("base64");
headers["X-PAYMENT"] = xPayment;Requirements:
- Content-Length header MUST be present when using X-PAYMENT
- Authorization value must match or exceed the 402 response's maxAmountRequired
- Nonce must be unique (32-byte random hex string)
Supported Networks: base, base-sepolia, ethereum-mainnet, polygon-mainnet
Standards:
- x402 Protocol: https://github.com/coinbase/x402
- EIP-3009: TransferWithAuthorization (gasless USDC transfers)
- EIP-712: Typed structured data signing
byteA signed ANS-104 data item (binary)
ANS-104 data item signed with Arweave, Ethereum, or Solana key
binaryResponse Body
curl -X POST "https://turbo.ardrive.io/x402/upload/signed" \ -H "content-length: 4294967296" \ -H "content-type: application/octet-stream" \ -H "X-PAYMENT: eyJ4NDAyVmVyc2lvbiI6MSwic2NoZW1lIjoiZXhhY3QiLCJuZXR3b3JrIjoiYmFzZSIsInBheWxvYWQiOnsic2lnbmF0dXJlIjoiMHgxMjM0NTY3ODkwYWJjZGVmLi4uIiwiYXV0aG9yaXphdGlvbiI6eyJmcm9tIjoiMHg3NDJkMzVDYzY2MzRDMDUzMjkyNWEzYjg0NEJjOWU3NTk1ZjBiRWIwIiwidG8iOiIweENGZDNmOTk2NDQ3YTU0MUNiZmJhNTQyMjMxMEVEYjQxN2Q5ZjJjRTYiLCJ2YWx1ZSI6IjMwNDkwIiwidmFsaWRBZnRlciI6MCwidmFsaWRCZWZvcmUiOjE3MzU2ODk2MDAsIm5vbmNlIjoiMHgxMjM0NTY3ODkwYWJjZGVmMTIzNDU2Nzg5MGFiY2RlZjEyMzQ1Njc4OTBhYmNkZWYxMjM0NTY3ODkwYWJjZGVmIn19fQ==" \ -H "Content-Type: application/octet-stream" \ -d 'string'{
"id": "QpmY8mZmFEC8RxNsgbxSV6e36OF6quIYaPRKzvUco0o",
"timestamp": 1700590909589,
"version": "0.2.0",
"owner": "0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb0",
"deadlineHeight": 1310000,
"dataCaches": [
"arweave.net"
],
"fastFinalityIndexes": [
"arweave.net"
],
"winc": "1000000",
"signature": "iU_S6uuG1OD8k0XqMGOmbcKfysDckEMUy4R9-ODPXiQj...",
"public": "qREovbmD6oxgHYNCzOeTei07lSz0-YLcjnvgSDzqpts...",
"x402Payment": {
"paymentId": "550e8400-e29b-41d4-a716-446655440000",
"transactionHash": "0xb8a52725cdc816d060392dcecaa1528872acf9ea0bd097a86e2432dd5d2213ca",
"network": "base",
"mode": "payg"
}
}"Data Item Exists""Invalid Content Type"{
"x402Version": 1,
"accepts": [
{
"scheme": "exact",
"network": "base",
"maxAmountRequired": "1000000",
"resource": "/v1/x402/upload/signed",
"description": "Upload 1024 bytes to Arweave via AR.IO Bundler",
"mimeType": "application/json",
"payTo": "0x6A0A10FFD285c971B841bee8892878c0d583Bf67",
"maxTimeoutSeconds": 300,
"asset": "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
"extra": {
"name": "USD Coin",
"version": "2"
}
}
],
"error": "Payment required"
}"Internal server error"Posts a signed ANS-104 data item with x402 payment (alias of /x402/upload/signed)
Alias of /x402/upload/signed. Identical behavior and handler
(dataItemRoute) — accepts a signed ANS-104 data item and processes an
x402 USDC payment. Provided as an alternate path for clients that prefer
the /x402/data-item/* naming. See /x402/upload/signed for the full
payment flow, requirements and supported networks.
Header Parameters
int64value <= 4294967296"application/octet-stream"Base64-encoded x402 payment authorization for pay-as-you-go USDC payments.
x402 Payment Flow:
- Upload without X-PAYMENT → Server returns 402 with payment requirements
- Client creates EIP-3009 authorization and signs with EIP-712
- Client retries upload with X-PAYMENT header containing base64(JSON)
- Server verifies signature, settles USDC transfer, processes upload
- Server returns receipt with x402Payment object and X-Payment-Response header
Decoded Structure (see X402PaymentHeader schema):
{
"x402Version": 1,
"scheme": "exact",
"network": "base",
"payload": {
"signature": "0x...(EIP-712 signature)...",
"authorization": {
"from": "0xPayerAddress",
"to": "0xBundlerPaymentAddress",
"value": "30490",
"validAfter": 0,
"validBefore": 1735689600,
"nonce": "0x...(32 bytes hex)..."
}
}
}Creating the Header:
const payload = { x402Version: 1, scheme: "exact", network: "base", payload: {...} };
const xPayment = Buffer.from(JSON.stringify(payload)).toString("base64");
headers["X-PAYMENT"] = xPayment;Requirements:
- Content-Length header MUST be present when using X-PAYMENT
- Authorization value must match or exceed the 402 response's maxAmountRequired
- Nonce must be unique (32-byte random hex string)
Supported Networks: base, base-sepolia, ethereum-mainnet, polygon-mainnet
Standards:
- x402 Protocol: https://github.com/coinbase/x402
- EIP-3009: TransferWithAuthorization (gasless USDC transfers)
- EIP-712: Typed structured data signing
byteA signed ANS-104 data item (binary)
ANS-104 data item signed with Arweave, Ethereum, or Solana key
binaryResponse Body
curl -X POST "https://turbo.ardrive.io/x402/data-item/signed" \ -H "content-length: 4294967296" \ -H "content-type: application/octet-stream" \ -H "X-PAYMENT: eyJ4NDAyVmVyc2lvbiI6MSwic2NoZW1lIjoiZXhhY3QiLCJuZXR3b3JrIjoiYmFzZSIsInBheWxvYWQiOnsic2lnbmF0dXJlIjoiMHgxMjM0NTY3ODkwYWJjZGVmLi4uIiwiYXV0aG9yaXphdGlvbiI6eyJmcm9tIjoiMHg3NDJkMzVDYzY2MzRDMDUzMjkyNWEzYjg0NEJjOWU3NTk1ZjBiRWIwIiwidG8iOiIweENGZDNmOTk2NDQ3YTU0MUNiZmJhNTQyMjMxMEVEYjQxN2Q5ZjJjRTYiLCJ2YWx1ZSI6IjMwNDkwIiwidmFsaWRBZnRlciI6MCwidmFsaWRCZWZvcmUiOjE3MzU2ODk2MDAsIm5vbmNlIjoiMHgxMjM0NTY3ODkwYWJjZGVmMTIzNDU2Nzg5MGFiY2RlZjEyMzQ1Njc4OTBhYmNkZWYxMjM0NTY3ODkwYWJjZGVmIn19fQ==" \ -H "Content-Type: application/octet-stream" \ -d 'string'{
"id": "QpmY8mZmFEC8RxNsgbxSV6e36OF6quIYaPRKzvUco0o",
"owner": "8wgRDgvYOrtSaWEIV21g0lTuWDUnTu4_iYj4hmA7PI0",
"payer": "0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb0",
"dataCaches": [
"arweave.net"
],
"fastFinalityIndexes": [
"arweave.net"
],
"deadlineHeight": 1310000,
"timestamp": 1700590909589,
"version": "0.1.0",
"signature": "iU_S6uuG1OD8k0XqMGOmbcKfysDckEMUy4R9-ODPXiQjKXuT4lTngRFBFKO5NQT1iIfqSDKcbTRL6gJowM_L7bBQZRGkojzXD0PNNU2F0bNNJ80VtUktHifGTXbCgz5kiFciL19n0P3nX6ZfXnOn-H8ALZzRJV69apdvwqitpNKxLMPyc-QA0QBxmC3CKPz_7fy2Qg0QHr5g_ZT2Of-YJ_RsZTEoc3g1fgzsEmMBPOsx4XtPrhV6llnA3pncngzHbPdFvypdWiO8Bvr0EWmazNsoanuwK5uKJ_ROIGXW_dBBGN8Vrfv5U6dJnhJVn5IE7JFlixpFTluF_ICRzbUq2pk_re6jEtW1H3ItH2iN0UeFUw1uDbq3HJW6lDc8aOwDwDspJI11KEI6uCz5QmQy2V8DvRknoqcxmuihF6XmmJIZgTVeo6LNufEis9kFxqtc3Dh_gn8z0cDXKEKFycudckmcHP7vkWD68uSssMMJIdVgwvPZss06svfRnI-E33j3MrQI9FzMIv-7Df8iYATyeyldM1v3gexG0kQm0AMG1_8_SLqwu2QlqzM41mrK5vNmQxOVdIQSOPWPvzbF-YGRwpCjlveRBuARGC9JNC4UipvDYri2gRWuBx2uDL7dmVFv1gRll3dYNMYaMULHYngtrrCynB3Cyfhh7cyPlwuNlk0",
"public": "qREovbmD6oxgHYNCzOeTei07lSz0-YLcjnvgSDzqptsCiqNOtB3RKUToSX5hkPD45fJDY4057XkkcsQRuGsU8y9rgm37i-Kiyd5Z_iy6pJXrwi9XnAgGL118lIV790GZ7xe5o3DvPV3Px74C0ABsfL9lW86D4t_qClJ6wSQksKNd7rnUImIvHW0vxLswST7dfUngevzKt4kv48VTub4951XdUHjb45Uurf7xFYSCizAGtGqr5GYDFrVk-mNrzFH5bXt06PJfxe9E5ujIE5Uq1Az6vqEOO0E1mWmXqdTPluAxcjmgktkoNLHZnnU-BsYuFaTWW5NU3aS-RgJKXYs9O6Dc1-2SITl-H_wtdGNSj31fj72UkaAbkpA1mionK-8bOIkSpYgKCyTC42oHh1Fw4SLXPyLxBj1w6F32LSLjpse5dmKymj4fJPezCMdi709uIiVT7XOm4LZBCzFOxS6-UNxgE57dBFpcWkcqNO3p00biYqH5d5bzvK3bwd-4j0KyKlqPYTProSnd3P6ROZuuJaLoLD_Or5-L_dUjawHz-DFlzmckYaf8l3XdzPM4JSsE8CEDZl0NCN0AidXt_wjbr6k9JsO7cnLB226AjxhcyuxZOlhgkIn7EbxpVXx-O2mXkcXF8PixCP0k5brtriLeF1MAdspgd_S-LKQeGVr7-mk",
"x402Payment": {
"paymentId": "550e8400-e29b-41d4-a716-446655440000",
"transactionHash": "0xb8a52725cdc816d060392dcecaa1528872acf9ea0bd097a86e2432dd5d2213ca",
"network": "base-mainnet",
"mode": "payg"
}
}"Data Item Exists""Invalid Content Type"{
"x402Version": 1,
"accepts": [
{}
],
"error": "Payment required"
}"Internal server error"Posts raw data with x402 payment (bundler creates ANS-104 wrapper)
x402-Specific Upload Endpoint for Raw Data (Server-Signed)
This endpoint accepts raw data (images, files, text, etc.) and the bundler creates and signs the ANS-104 data item wrapper server-side. Payment is via x402 USDC.
Use Case: AI agents, simple apps, and clients that don't want to implement ANS-104 signing.
Payment Flow
1. POST raw data (no X-PAYMENT header)
↓
2. Server returns 402 with payment requirements
↓
3. Client creates EIP-3009 authorization + EIP-712 signature
↓
4. POST raw data WITH X-PAYMENT header
↓
5. Server: verify → settle USDC → create data item → store → return receiptPricing
Formula:
estimatedSize = rawDataSize + 512 (signature) + 512 (owner) + 80 (headers) + (totalTags × 64)
winstonCost = arweaveGateway.getPrice(estimatedSize)
usdcAmount = (winstonCost / 10^12) × arPriceUSD × 10^6 × (1 + feePercent/100)Fee: Configurable via X402_PRICING_BUFFER_PERCENT (default: 15%)
Get price quote first: GET /v1/price/x402/data/{token}/{byteCount}?tags={n}&contentType={mime}
System Tags (Automatically Added)
| Tag | Description | Example |
|---|---|---|
| Bundler | Service name | "AR.IO Bundler" |
| Upload-Type | Upload method | "raw-data-x402" |
| Payer-Address | Ethereum payer | "0x742d35Cc..." |
| X402-TX-Hash | Blockchain tx | "0x9c31110e..." |
| X402-Payment-ID | Tracking UUID | "edd4766e-..." |
| X402-Network | Payment network | "base" |
| Upload-Timestamp | Unix ms | "1700590909589" |
| Content-Type | MIME type | "image/png" |
Custom Tags
Via Headers: X-Tag-App-Name: MyApp → {"name": "App-Name", "value": "MyApp"}
Via JSON Envelope: {"data": "...", "tags": [{"name": "App-Name", "value": "MyApp"}]}
Note: Header tag names are converted from kebab-case to Proper-Case.
Important Notes
- Ownership: The
ownerfield is the bundler's server wallet. The payer's address is recorded in thePayer-Addresstag only. - Server Requirement: Requires
RAW_DATA_UPLOADS_ENABLED=trueon server - Max Size: 10 GB
- Max Tags: 100 user tags
Supported Networks
| Network | Token | Status | USDC Contract |
|---|---|---|---|
| Base mainnet | usdc-base | Default | 0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913 |
| Base Sepolia | usdc-base-sepolia | Requires env | 0x036CbD53842c5426634e7929541eC2318f3dCF7e |
| Ethereum | usdc-ethereum-mainnet | Requires env | 0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48 |
| Polygon | usdc-polygon-mainnet | Requires env | 0x3c499c542cEF5E3811e1192ce70d8cC03d5c3359 |
Header Parameters
int64value <= 4294967296MIME type of the raw data (e.g., image/png, application/pdf)
Base64-encoded x402 payment authorization for pay-as-you-go USDC payments.
x402 Payment Flow:
- Upload without X-PAYMENT → Server returns 402 with payment requirements
- Client creates EIP-3009 authorization and signs with EIP-712
- Client retries upload with X-PAYMENT header containing base64(JSON)
- Server verifies signature, settles USDC transfer, processes upload
- Server returns receipt with x402Payment object and X-Payment-Response header
Decoded Structure (see X402PaymentHeader schema):
{
"x402Version": 1,
"scheme": "exact",
"network": "base",
"payload": {
"signature": "0x...(EIP-712 signature)...",
"authorization": {
"from": "0xPayerAddress",
"to": "0xBundlerPaymentAddress",
"value": "30490",
"validAfter": 0,
"validBefore": 1735689600,
"nonce": "0x...(32 bytes hex)..."
}
}
}Creating the Header:
const payload = { x402Version: 1, scheme: "exact", network: "base", payload: {...} };
const xPayment = Buffer.from(JSON.stringify(payload)).toString("base64");
headers["X-PAYMENT"] = xPayment;Requirements:
- Content-Length header MUST be present when using X-PAYMENT
- Authorization value must match or exceed the 402 response's maxAmountRequired
- Nonce must be unique (32-byte random hex string)
Supported Networks: base, base-sepolia, ethereum-mainnet, polygon-mainnet
Standards:
- x402 Protocol: https://github.com/coinbase/x402
- EIP-3009: TransferWithAuthorization (gasless USDC transfers)
- EIP-712: Typed structured data signing
byteCustom tags in the format X-TAG-Name: Value. Multiple tags can be specified. Tag names are case-insensitive.
Raw data to upload. Supports two formats:
1. Binary Upload (Recommended for files)
- Content-Type: application/octet-stream, image/png, application/pdf, etc.
- Body: Raw binary data
- Custom tags via X-Tag-* headers
2. JSON Envelope (Alternative for programmatic use)
- Content-Type: application/json
- Body:
{"data": "<base64>", "contentType": "...", "tags": [...]} - Useful when headers are difficult to set (e.g., some HTTP clients)
Raw binary data (use X-Tag-* headers for custom tags)
binaryResponse Body
curl -X POST "https://turbo.ardrive.io/x402/upload/unsigned" \ -H "content-length: 4294967296" \ -H "Content-Type: image/png" \ -H "X-PAYMENT: eyJ4NDAyVmVyc2lvbiI6MSwic2NoZW1lIjoiZXhhY3QiLCJuZXR3b3JrIjoiYmFzZSIsInBheWxvYWQiOnsic2lnbmF0dXJlIjoiMHgxMjM0NTY3ODkwYWJjZGVmLi4uIiwiYXV0aG9yaXphdGlvbiI6eyJmcm9tIjoiMHg3NDJkMzVDYzY2MzRDMDUzMjkyNWEzYjg0NEJjOWU3NTk1ZjBiRWIwIiwidG8iOiIweENGZDNmOTk2NDQ3YTU0MUNiZmJhNTQyMjMxMEVEYjQxN2Q5ZjJjRTYiLCJ2YWx1ZSI6IjMwNDkwIiwidmFsaWRBZnRlciI6MCwidmFsaWRCZWZvcmUiOjE3MzU2ODk2MDAsIm5vbmNlIjoiMHgxMjM0NTY3ODkwYWJjZGVmMTIzNDU2Nzg5MGFiY2RlZjEyMzQ1Njc4OTBhYmNkZWYxMjM0NTY3ODkwYWJjZGVmIn19fQ==" \ -H "X-TAG-*: string" \ -H "Content-Type: application/json" \ -d '{ "data": "SGVsbG8gV29ybGQh", "contentType": "text/plain" }'{
"id": "rNephs2z5BAAUlmZm_TGwQjz4TiMr6HoZNyBO3eLctA",
"owner": "jHGQATyLh_yBNKwqPmU-y6qV98q-AB48fIWUiFOSJAA",
"payer": "0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb0",
"dataCaches": [
"arweave.net"
],
"fastFinalityIndexes": [
"arweave.net"
],
"receipt": {
"id": "rNephs2z5BAAUlmZm_TGwQjz4TiMr6HoZNyBO3eLctA",
"timestamp": 1700590909589,
"version": "0.2.0",
"deadlineHeight": 1310000,
"dataCaches": [
"arweave.net"
],
"fastFinalityIndexes": [
"arweave.net"
],
"winc": "1000000",
"public": "qREovbmD6oxgHYNCzOeTei07lSz0-YLcjnvgSDzqpts...",
"signature": "iU_S6uuG1OD8k0XqMGOmbcKfysDckEMUy4R9-ODPXiQj..."
},
"x402Payment": {
"paymentId": "edd4766e-a053-47f2-88d1-819e72c534dd",
"transactionHash": "0x9c31110e240805d9dfa5853c661f23a258c89f78b8d2e4424a922f9dfd8748f3",
"network": "base",
"mode": "payg"
}
}"Invalid data. Data size exceeds maximum allowed size of 4294967296 bytes (4 GiB)"{
"x402Version": 1,
"accepts": [
{
"scheme": "exact",
"network": "base",
"maxAmountRequired": "3508",
"resource": "https://upload.yourdomain.com/v1/x402/upload/unsigned",
"description": "Upload data to Arweave via AR.IO Bundler",
"mimeType": "application/json",
"payTo": "0xCFd3f996447a541Cbfba5422310EDb417d9f2cE6",
"maxTimeoutSeconds": 3600,
"asset": "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913"
}
]
}"Raw data uploads are not enabled on this bundler""Upload Service is Unavailable. Payment Service is unreachable"Creates a new multi-part upload. Chunks should be posted to /chunks/:token/:uploadId/:chunkOffset using the returned uploadId. ARx compatible.
Path Parameters
The token to use for validating the transaction.
"arweave" | "ethereum" | "solana"Response Body
curl -X GET "https://turbo.ardrive.io/chunks/arweave/-1/-1"{
"id": "QpmY8mZmFEC8RxNsgbxSV6e36OF6quIYaPRKzvUco0o",
"min": 2500,
"max": "500_000_000"
}"Internal server error"Gets an existing multi-part upload, including all existing chunks that have been uploaded. ARx compatible.
Path Parameters
The upload id of the multi-part upload.
The token to use for validating the transaction.
"arweave" | "ethereum" | "solana"Response Body
curl -X GET "https://turbo.ardrive.io/chunks/:token/:uploadId/-1"{
"id": "QpmY8mZmFEC8RxNsgbxSV6e36OF6quIYaPRKzvUco0o",
"min": 2500,
"max": 500000000,
"size": 25000000,
"chunks": [
[
25000000,
25000000
],
[
50000000,
25000000
],
[
75000000,
25000000
],
[
100000000,
4858676
]
]
}"Multi-part upload not found""Internal server error"Finalizes a multi-part upload. ARx compatible.
Path Parameters
The upload id of the multi-part upload.
The token to use for validating the transaction.
"arweave" | "ethereum" | "solana"Response Body
curl -X POST "https://turbo.ardrive.io/chunks/:token/:uploadId/-1"{
"id": "QpmY8mZmFEC8RxNsgbxSV6e36OF6quIYaPRKzvUco0o",
"data": {
"id": "QpmY8mZmFEC8RxNsgbxSV6e36OF6quIYaPRKzvUco0o",
"owner": "8wgRDgvYOrtSaWEIV21g0lTuWDUnTu4_iYj4hmA7PI0",
"payer": "0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb0",
"dataCaches": [
"arweave.net"
],
"fastFinalityIndexes": [
"arweave.net"
],
"deadlineHeight": 1310000,
"timestamp": 1700590909589,
"version": "0.1.0",
"signature": "iU_S6uuG1OD8k0XqMGOmbcKfysDckEMUy4R9-ODPXiQjKXuT4lTngRFBFKO5NQT1iIfqSDKcbTRL6gJowM_L7bBQZRGkojzXD0PNNU2F0bNNJ80VtUktHifGTXbCgz5kiFciL19n0P3nX6ZfXnOn-H8ALZzRJV69apdvwqitpNKxLMPyc-QA0QBxmC3CKPz_7fy2Qg0QHr5g_ZT2Of-YJ_RsZTEoc3g1fgzsEmMBPOsx4XtPrhV6llnA3pncngzHbPdFvypdWiO8Bvr0EWmazNsoanuwK5uKJ_ROIGXW_dBBGN8Vrfv5U6dJnhJVn5IE7JFlixpFTluF_ICRzbUq2pk_re6jEtW1H3ItH2iN0UeFUw1uDbq3HJW6lDc8aOwDwDspJI11KEI6uCz5QmQy2V8DvRknoqcxmuihF6XmmJIZgTVeo6LNufEis9kFxqtc3Dh_gn8z0cDXKEKFycudckmcHP7vkWD68uSssMMJIdVgwvPZss06svfRnI-E33j3MrQI9FzMIv-7Df8iYATyeyldM1v3gexG0kQm0AMG1_8_SLqwu2QlqzM41mrK5vNmQxOVdIQSOPWPvzbF-YGRwpCjlveRBuARGC9JNC4UipvDYri2gRWuBx2uDL7dmVFv1gRll3dYNMYaMULHYngtrrCynB3Cyfhh7cyPlwuNlk0",
"public": "qREovbmD6oxgHYNCzOeTei07lSz0-YLcjnvgSDzqptsCiqNOtB3RKUToSX5hkPD45fJDY4057XkkcsQRuGsU8y9rgm37i-Kiyd5Z_iy6pJXrwi9XnAgGL118lIV790GZ7xe5o3DvPV3Px74C0ABsfL9lW86D4t_qClJ6wSQksKNd7rnUImIvHW0vxLswST7dfUngevzKt4kv48VTub4951XdUHjb45Uurf7xFYSCizAGtGqr5GYDFrVk-mNrzFH5bXt06PJfxe9E5ujIE5Uq1Az6vqEOO0E1mWmXqdTPluAxcjmgktkoNLHZnnU-BsYuFaTWW5NU3aS-RgJKXYs9O6Dc1-2SITl-H_wtdGNSj31fj72UkaAbkpA1mionK-8bOIkSpYgKCyTC42oHh1Fw4SLXPyLxBj1w6F32LSLjpse5dmKymj4fJPezCMdi709uIiVT7XOm4LZBCzFOxS6-UNxgE57dBFpcWkcqNO3p00biYqH5d5bzvK3bwd-4j0KyKlqPYTProSnd3P6ROZuuJaLoLD_Or5-L_dUjawHz-DFlzmckYaf8l3XdzPM4JSsE8CEDZl0NCN0AidXt_wjbr6k9JsO7cnLB226AjxhcyuxZOlhgkIn7EbxpVXx-O2mXkcXF8PixCP0k5brtriLeF1MAdspgd_S-LKQeGVr7-mk",
"x402Payment": {
"paymentId": "550e8400-e29b-41d4-a716-446655440000",
"transactionHash": "0xb8a52725cdc816d060392dcecaa1528872acf9ea0bd097a86e2432dd5d2213ca",
"network": "base-mainnet",
"mode": "payg"
}
}
}"Insufficient balance""Multi-part upload not found""Internal server error"Finalizes a multi-part upload asynchronously.
Path Parameters
The upload id of the multi-part upload.
The token to use for validating the transaction.
"arweave" | "ethereum" | "solana"Response Body
curl -X POST "https://turbo.ardrive.io/chunks/:token/:uploadId/finalize"{
"id": "QpmY8mZmFEC8RxNsgbxSV6e36OF6quIYaPRKzvUco0o",
"data": {
"id": "QpmY8mZmFEC8RxNsgbxSV6e36OF6quIYaPRKzvUco0o",
"owner": "8wgRDgvYOrtSaWEIV21g0lTuWDUnTu4_iYj4hmA7PI0",
"payer": "0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb0",
"dataCaches": [
"arweave.net"
],
"fastFinalityIndexes": [
"arweave.net"
],
"deadlineHeight": 1310000,
"timestamp": 1700590909589,
"version": "0.1.0",
"signature": "iU_S6uuG1OD8k0XqMGOmbcKfysDckEMUy4R9-ODPXiQjKXuT4lTngRFBFKO5NQT1iIfqSDKcbTRL6gJowM_L7bBQZRGkojzXD0PNNU2F0bNNJ80VtUktHifGTXbCgz5kiFciL19n0P3nX6ZfXnOn-H8ALZzRJV69apdvwqitpNKxLMPyc-QA0QBxmC3CKPz_7fy2Qg0QHr5g_ZT2Of-YJ_RsZTEoc3g1fgzsEmMBPOsx4XtPrhV6llnA3pncngzHbPdFvypdWiO8Bvr0EWmazNsoanuwK5uKJ_ROIGXW_dBBGN8Vrfv5U6dJnhJVn5IE7JFlixpFTluF_ICRzbUq2pk_re6jEtW1H3ItH2iN0UeFUw1uDbq3HJW6lDc8aOwDwDspJI11KEI6uCz5QmQy2V8DvRknoqcxmuihF6XmmJIZgTVeo6LNufEis9kFxqtc3Dh_gn8z0cDXKEKFycudckmcHP7vkWD68uSssMMJIdVgwvPZss06svfRnI-E33j3MrQI9FzMIv-7Df8iYATyeyldM1v3gexG0kQm0AMG1_8_SLqwu2QlqzM41mrK5vNmQxOVdIQSOPWPvzbF-YGRwpCjlveRBuARGC9JNC4UipvDYri2gRWuBx2uDL7dmVFv1gRll3dYNMYaMULHYngtrrCynB3Cyfhh7cyPlwuNlk0",
"public": "qREovbmD6oxgHYNCzOeTei07lSz0-YLcjnvgSDzqptsCiqNOtB3RKUToSX5hkPD45fJDY4057XkkcsQRuGsU8y9rgm37i-Kiyd5Z_iy6pJXrwi9XnAgGL118lIV790GZ7xe5o3DvPV3Px74C0ABsfL9lW86D4t_qClJ6wSQksKNd7rnUImIvHW0vxLswST7dfUngevzKt4kv48VTub4951XdUHjb45Uurf7xFYSCizAGtGqr5GYDFrVk-mNrzFH5bXt06PJfxe9E5ujIE5Uq1Az6vqEOO0E1mWmXqdTPluAxcjmgktkoNLHZnnU-BsYuFaTWW5NU3aS-RgJKXYs9O6Dc1-2SITl-H_wtdGNSj31fj72UkaAbkpA1mionK-8bOIkSpYgKCyTC42oHh1Fw4SLXPyLxBj1w6F32LSLjpse5dmKymj4fJPezCMdi709uIiVT7XOm4LZBCzFOxS6-UNxgE57dBFpcWkcqNO3p00biYqH5d5bzvK3bwd-4j0KyKlqPYTProSnd3P6ROZuuJaLoLD_Or5-L_dUjawHz-DFlzmckYaf8l3XdzPM4JSsE8CEDZl0NCN0AidXt_wjbr6k9JsO7cnLB226AjxhcyuxZOlhgkIn7EbxpVXx-O2mXkcXF8PixCP0k5brtriLeF1MAdspgd_S-LKQeGVr7-mk",
"x402Payment": {
"paymentId": "550e8400-e29b-41d4-a716-446655440000",
"transactionHash": "0xb8a52725cdc816d060392dcecaa1528872acf9ea0bd097a86e2432dd5d2213ca",
"network": "base-mainnet",
"mode": "payg"
}
}
}"Insufficient balance""Multi-part upload not found""Internal server error"Gets the status of a multi-part upload.
Path Parameters
The token to use for validating the transaction.
"arweave" | "ethereum" | "solana"The upload id of the multi-part upload.
Response Body
curl -X GET "https://turbo.ardrive.io/chunks/:token/:uploadId/status"{
"status": "VALIDATING",
"timestamp": 1700590909589
}"Multi-part upload not found""Internal server error"Posts a chunk of a multi-part upload. ARx compatible.
Path Parameters
The upload id of the multi-part upload.
The token to use for validating the transaction.
"arweave" | "ethereum" | "solana"The offset of the chunk in bytes. If -1, that will finalize an existing multi-part upload.
A chunk of a multi-part upload
binaryResponse Body
curl -X POST "https://turbo.ardrive.io/chunks/:token/:uploadId/:chunkOffset" \ -H "Content-Type: application/octet-stream" \ -d 'string'"Insufficient balance""Multi-part upload not found""Internal server error"How is this guide?