Quickstart
Send your first Corvo shipment in three API calls: upload a PDF, create a draft shipment to get live rates, and buy the quote you want.
Step 1: Upload a PDF
Upload the PDF you want Corvo to print and mail. The response returns a document_key that you use in shipment creation.
/api/v1/documents/uploadUpload a PDF document via multipart form data.
curl -X POST https://corvo.to/api/v1/documents/upload \
-H "Authorization: Bearer $CORVO_API_KEY" \
-F file=@lease-agreement.pdf{
"data": {
"document_key": "documents/a3b8c1d2-e4f5-6789-abcd-ef0123456789/lease-agreement.pdf",
"filename": "lease-agreement.pdf",
"page_count": 8,
"size_bytes": 245760
}
}Step 2: Create a draft shipment
Create a shipment with the uploaded document, addresses, and print options. Corvo always returns draft quotes first so you can choose the rate you want before buying.
/api/v1/shipmentsCreate a draft shipment and return live rate quotes.
curl -X POST https://corvo.to/api/v1/shipments \
-H "Authorization: Bearer $CORVO_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"document_key": "documents/a3b8c1d2-e4f5-6789-abcd-ef0123456789/lease-agreement.pdf",
"shipment_name": "Lease Agreement",
"to_address": {
"name": "Jordan Reed",
"street1": "123 Main St",
"city": "Austin",
"state": "TX",
"zip": "78701"
},
"from_address": {
"name": "Acme Property Management",
"street1": "500 Congress Ave",
"city": "Austin",
"state": "TX",
"zip": "78701"
},
"print_options": {
"color": false,
"duplex": true
},
"shipping_options": {
"certified_mail": true,
"return_receipt": true
}
}'{
"data": {
"id": "f47ac10b-58cc-4372-a567-0e02b2c3d479",
"status": "draft",
"shipment_name": "Lease Agreement",
"page_count": 8,
"shipping_options": {
"delivery_confirmation": null,
"certified_mail": true,
"return_receipt": true
},
"rates": [
{
"quote_id": "9f0a1b2c-3d4e-4f5a-8b9c-0d1e2f3a4b5c",
"carrier": "USPS",
"service": "Priority",
"postage_cents": 855,
"handling_fee_cents": 300,
"printing_fee_cents": 200,
"color_surcharge_cents": 0,
"total_cents": 1355,
"delivery_days": 2,
"delivery_date": "2026-03-11T00:00:00.000Z",
"delivery_date_guaranteed": false
}
],
"quotes_expire_at": "2026-03-09T15:30:00.000Z"
}
}quote_id returned by the draft response.Step 3: Buy the selected quote
Choose one of the returned quote IDs and pass it to the buy endpoint. Corvo charges the organization card on file and moves the shipment into fulfillment.
/api/v1/shipments/{id}/buyBuy a draft shipment using a selected quote.
curl -X POST https://corvo.to/api/v1/shipments/f47ac10b-58cc-4372-a567-0e02b2c3d479/buy \
-H "Authorization: Bearer $CORVO_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"quote_id": "9f0a1b2c-3d4e-4f5a-8b9c-0d1e2f3a4b5c"
}'{
"data": {
"id": "f47ac10b-58cc-4372-a567-0e02b2c3d479",
"status": "queued",
"carrier": "USPS",
"service": "Priority",
"total_charged_cents": 1355,
"tracking_number": "9405511899223033005084",
"tracking_url": "https://tools.usps.com/go/TrackConfirmAction?tLabels=9405511899223033005084"
}
}Step 4: Poll shipment status
Use shipment detail responses for fulfillment status, tracking, proof artifacts, mailing-record state, and event history. When you poll with an API key, Corvo limits this endpoint to one request per shipment every five minutes and may return a cached response inside that window.
curl https://corvo.to/api/v1/shipments/f47ac10b-58cc-4372-a567-0e02b2c3d479 \
-H "Authorization: Bearer $CORVO_API_KEY"{
"data": {
"id": "f47ac10b-58cc-4372-a567-0e02b2c3d479",
"status": "queued",
"carrier": "USPS",
"service": "Priority",
"shipping_options": {
"delivery_confirmation": null,
"certified_mail": true,
"return_receipt": false
},
"tracking_number": "9405511899223033005084",
"tracking_url": "https://tools.usps.com/go/TrackConfirmAction?tLabels=9405511899223033005084",
"total_charged_cents": 1355,
"created_at": "2026-03-09T15:00:00.000Z",
"shipped_at": null,
"mailing_records": {
"acceptance": {
"requested": true,
"manifested": false,
"accepted_at": null,
"source": null,
"scan_form_available": false
},
"delivery": {
"latest_status": "pre_transit",
"latest_status_detail": "label_created",
"delivered_at": null,
"attempted_at": null,
"signed_by": null,
"returned_to_sender_at": null
},
"return_receipt": {
"requested": false,
"status": "not_requested",
"artifact_id": null,
"available_at": null
},
"retention": {
"evidence_expires_at": "2036-03-09T15:00:00.000Z",
"document_expires_at": "2028-03-08T15:00:00.000Z",
"original_document_retained": true
}
},
"artifacts": [
{
"id": "mailing-record-summary",
"kind": "mailing_record_summary_pdf",
"provenance": "corvo_generated",
"download_url": "/api/v1/shipments/f47ac10b-58cc-4372-a567-0e02b2c3d479/proof"
}
],
"events": [
{
"id": "evt_01",
"status": "payment_pending",
"from_status": "draft",
"message": "Payment initiated for draft shipment",
"actor": "user:4c1b8b16-2e4f-4de0-9ad5-e0ec0f16dbe8",
"metadata": null,
"created_at": "2026-03-09T15:00:04.000Z"
}
]
}
}End-to-end TypeScript example
import { readFileSync } from "node:fs";
const API_KEY = process.env.CORVO_API_KEY!;
const BASE_URL = "https://corvo.to/api/v1";
async function main() {
const file = new Blob([readFileSync("lease-agreement.pdf")], {
type: "application/pdf",
});
const uploadForm = new FormData();
uploadForm.append("file", file, "lease-agreement.pdf");
const uploadResponse = await fetch(`${BASE_URL}/documents/upload`, {
method: "POST",
headers: { Authorization: `Bearer ${API_KEY}` },
body: uploadForm,
});
const uploadJson = await uploadResponse.json();
const documentKey = uploadJson.data.document_key;
const draftResponse = await fetch(`${BASE_URL}/shipments`, {
method: "POST",
headers: {
Authorization: `Bearer ${API_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
document_key: documentKey,
shipment_name: "Lease Agreement",
to_address: {
name: "Jordan Reed",
street1: "123 Main St",
city: "Austin",
state: "TX",
zip: "78701",
},
print_options: { color: false, duplex: true },
shipping_options: { certified_mail: true, return_receipt: true },
}),
});
const draftJson = await draftResponse.json();
const quote = draftJson.data.rates[0];
const buyResponse = await fetch(
`${BASE_URL}/shipments/${draftJson.data.id}/buy`,
{
method: "POST",
headers: {
Authorization: `Bearer ${API_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify({ quote_id: quote.quote_id }),
},
);
const buyJson = await buyResponse.json();
console.log({
shipmentId: buyJson.data.id,
status: buyJson.data.status,
trackingNumber: buyJson.data.tracking_number,
});
}
main().catch((error) => {
console.error(error);
process.exit(1);
});