Load Cargo
Submit a request for batch processing.
POST /cargo/loadAuthentication
This endpoint requires the X-API-Key header with a valid project API key.
-H "X-API-Key: convoy_sk_your_key_here"See Authentication for details on obtaining API keys.
Request Body
{
"params": {
"model": "claude-3-haiku",
"max_tokens": 1024,
"messages": [
{"role": "user", "content": "Your prompt here"}
],
"system": "Optional system prompt",
"temperature": 0.7,
"top_p": 0.9,
"top_k": 50,
"stop_sequences": ["STOP"]
},
"callback_url": "https://your-server.com/callback",
"metadata": {
"order_id": "ord_8842",
"de_callback_id": "AQICAHh..."
}
}Parameters
| Field | Type | Required | Description |
|---|---|---|---|
params.model | string | Yes | Provider-agnostic model ID (e.g., claude-3-haiku) |
params.max_tokens | integer | Yes (chat) | Max tokens to generate. Required for chat requests (messages); ignored for embedding requests (input_text). Must be a positive integer |
params.messages | array | Yes | Conversation messages |
params.system | string | No | System prompt |
params.temperature | float | No | Sampling temperature (0.0-1.0) |
params.top_p | float | No | Top-p sampling (0.0-1.0) |
params.top_k | integer | No | Top-k sampling |
params.stop_sequences | array | No | Stop sequences |
callback_url | string | No | HTTPS URL to receive results via webhook. Omit for “mailbox-only” submissions — no webhook is sent and you fetch the result with GET /cargo/{cargo_id}/result. Mutually exclusive with callback |
callback | object | No | Structured callback target for direct AWS delivery, discriminated on type: aws_durable_callback ({"type": "aws_durable_callback", "connection_id": "awsconn_…", "callback_id": "<your durable callback ID>"} — resumes your durable Lambda execution), aws_sfn_task_token ({"type": "aws_sfn_task_token", "connection_id": "awsconn_…", "task_token": "<$$.Task.Token>"} — resumes your Step Functions .waitForTaskToken wait), aws_lambda_invoke ({"type": "aws_lambda_invoke", "connection_id": "awsconn_…", "function_arn": "arn:aws:lambda:…:function:on-cargo-done:$LATEST"} — invokes your Lambda function asynchronously with the result envelope; the ARN must be qualified with :$LATEST, a version, or an alias), aws_eventbridge ({"type": "aws_eventbridge", "connection_id": "awsconn_…", "event_bus_arn": "arn:aws:events:…:event-bus/my-bus"} — publishes a source=convoy.cargo event onto your bus), aws_sqs ({"type": "aws_sqs", "connection_id": "awsconn_…", "queue_arn": "arn:aws:sqs:…:cargo-results"} — sends the envelope to your queue; FIFO queues require message_group_id), aws_kinesis ({"type": "aws_kinesis", "connection_id": "awsconn_…", "stream_arn": "arn:aws:kinesis:…:stream/cargo-results"} — puts the envelope on your stream; optional partition_key, defaults to the cargo_id), aws_sfn_start ({"type": "aws_sfn_start", "connection_id": "awsconn_…", "state_machine_arn": "arn:aws:states:…:stateMachine:handle-result"} — starts a NEW execution named convoy-{cargo_id} with the envelope as input), or aws_s3 ({"type": "aws_s3", "connection_id": "awsconn_…", "bucket_arn": "arn:aws:s3:::acme-cargo-results", "key_prefix": "convoy/"} — writes the FULL result, no size cap, as {key_prefix}{cargo_id}.json in your bucket; write-only and prefix-scoped). No webhook endpoint needed. Mutually exclusive with callback_url |
result_ttl_seconds | integer | No | How long the stored result stays retrievable via /result — 3,600 (1 hour) to 2,592,000 (30 days). Default: 30 days |
metadata | object | No | Up to 50 string-to-string pairs, echoed back verbatim in the callback payload and the /result body. Opaque to Convoy — never interpreted, logged, or indexed |
Breaking change: max_tokens is now required for chat requests and
no longer defaults to 1024. Requests that omit it are rejected with a 422
validation error ("max_tokens is required for chat requests"). Set it to a
realistic ceiling for your workload — it caps the model’s output and is
used to estimate the credit hold placed while the batch is in flight, so a
tighter value means a smaller hold against your available balance.
See Supported Models for the full list of available model IDs.
Getting your results
Every result is stored server-side (30 days by default) regardless of how you submit. You have two delivery options — use either or both:
- Webhook — provide a
callback_urland Convoy POSTs the result to you when processing completes (with retries). - Mailbox — omit
callback_urland pollGET /cargo/{cargo_id}/resultinstead. No public endpoint needed; the cargo terminates atcompleted/failed. - Direct AWS delivery — provide a
callbackobject naming a verified AWS connection and Convoy delivers directly:type: "aws_durable_callback"resumes your suspended durable Lambda execution viaSendDurableExecutionCallbackSuccess/Failure;type: "aws_sfn_task_token"resumes your Step Functions.waitForTaskTokenwait viaSendTaskSuccess/Failure;type: "aws_lambda_invoke"invokes your Lambda function asynchronously with the result envelope;type: "aws_eventbridge"publishes an event onto your bus for your rules to fan out;type: "aws_sqs"/type: "aws_kinesis"deliver to your queue or data stream;type: "aws_sfn_start"starts a new state machine execution with the envelope as input;type: "aws_s3"writes the FULL result as an object in your bucket — no size cap, write-only, prefix-scoped. No webhook, no receiver.
The webhook body and the /result body have the same shape, so your
parsing code is delivery-agnostic. Even with a callback_url, /result
remains available as a fallback (e.g. after callback_failed).
Metadata
Attach your own correlation data to a request with the optional top-level
metadata object. Convoy stores it verbatim and echoes it back in the
callback payload — it is never interpreted, logged, or indexed. Typical uses:
correlating a callback with a record in your system (order_id,
trace_id), or carrying a resume token for a workflow engine (e.g. an
AWS Lambda durable function callback ID).
Limits (requests that exceed them are rejected with a 422):
| Constraint | Limit |
|---|---|
| Entries | 50 |
| Key length | 128 characters |
| Value length | 2,048 characters |
| Total serialized size | 4,096 bytes (JSON-encoded) |
| Types | String keys and string values only |
Metadata values may carry secrets or capability tokens, so Convoy treats them as sensitive: validation errors reference the offending key only, and values never appear in Convoy’s logs.
Message Format
{
"role": "user",
"content": "Message text"
}Or with content blocks:
{
"role": "user",
"content": [
{"type": "text", "text": "Message text"}
]
}Multimodal Content Blocks
Messages can reference uploaded files (images, PDFs, documents, and video)
alongside text using image, document, and video content blocks. Upload
the file either from your project’s Files tab in the dashboard or
programmatically via the Files API (POST /files → presigned
direct-to-storage upload → POST /files/{id}/complete), then reference its
file_id in the request. Two Files API branches to handle: if POST /files
returns an existing identical file (deduplicated: true, upload: null),
skip the upload and completion steps and use its id directly; and
POST /files/{id}/complete may return 202 (status: "validating") while
the malware scan is pending — keep polling while the status is pending and
use the file once it reaches ready. A failed status is terminal
(e.g. malware detected, checksum mismatch, scan timeout): stop polling and
surface the error instead of referencing the file:
{
"role": "user",
"content": [
{"type": "text", "text": "Summarize this document"},
{"type": "document", "source": {"file_id": "file_0123456789abcdef0123456789abcdef"}}
]
}Only models with the matching capability accept these blocks — look for the
Vision, Documents, and Video badges on the
models page . Referenced files must be uploaded to
the same project and finished processing (status ready); otherwise the
request is rejected.
Plan availability (Convoy Cloud): file uploads are available on the
Starter and Pro plans. On the Free plan, creating files returns
403 files_not_allowed_for_plan and the dashboard shows an upgrade prompt
instead of the upload widget — upgrade from Dashboard → Billing to
enable uploads. Self-hosted Enterprise deployments are not plan-gated;
Files limits come from your license entitlements.
Response
{
"cargo_id": "cargo_9f4c1b2a3d4e5f60718293a4b5c6d7e8",
"status": "success",
"message": "Cargo loaded successfully"
}| Field | Description |
|---|---|
cargo_id | Unique identifier for tracking |
status | success or error |
message | Result description |
Examples
curl
curl -X POST https://api.cnvy.ai/cargo/load \
-H "Content-Type: application/json" \
-H "X-API-Key: convoy_sk_your_key_here" \
-d '{
"params": {
"model": "claude-3-haiku",
"max_tokens": 100,
"messages": [{"role": "user", "content": "Hello"}]
},
"callback_url": "https://example.com/callback"
}'Python
import httpx
response = httpx.post(
"https://api.cnvy.ai/cargo/load",
headers={
"X-API-Key": "convoy_sk_your_key_here"
},
json={
"params": {
"model": "claude-3-haiku",
"max_tokens": 100,
"messages": [{"role": "user", "content": "Hello"}]
},
"callback_url": "https://example.com/callback"
}
)
data = response.json()
cargo_id = data["cargo_id"]
print(f"Submitted: {cargo_id}")JavaScript
const response = await fetch("https://api.cnvy.ai/cargo/load", {
method: "POST",
headers: {
"Content-Type": "application/json",
"X-API-Key": "convoy_sk_your_key_here"
},
body: JSON.stringify({
params: {
model: "claude-3-haiku",
max_tokens: 100,
messages: [{ role: "user", content: "Hello" }]
},
callback_url: "https://example.com/callback"
})
});
const { cargo_id } = await response.json();
console.log(`Submitted: ${cargo_id}`);Errors
| Status | Description |
|---|---|
400 | Invalid request body or invalid model ID |
401 | Missing or invalid API key |
403 | Project is inactive |
422 | Validation error (e.g. max_tokens missing on a chat request) |
429 | Rate limit or quota exceeded (rate_limit_exceeded / quota_exceeded) |
500 | Internal server error |
Error Response Examples
Missing API Key:
{"detail": "API key required. Include X-API-Key header."}Invalid API Key:
{"detail": "Invalid API key."}Inactive Project:
{"detail": "Project is inactive."}Missing max_tokens (chat request):
{
"detail": [
{
"type": "value_error",
"loc": ["body", "params"],
"msg": "Value error, max_tokens is required for chat requests"
}
]
}Invalid Model ID:
{
"detail": {
"error": "invalid_model",
"message": "Unknown model: claude-4-turbo",
"supported_models": ["claude-3-haiku", "claude-3-sonnet", "claude-3-opus", "..."]
}
}