curl --request POST \
--url https://catchbackcards.com/api/partner/v1/shipments \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--header 'Idempotency-Key: <idempotency-key>' \
--data '
{
"card_ids": [
"0e94dc61-5a2f-4b1e-9c3d-7f8a1b2c3d4e"
],
"address": {
"name": "Sam Rivera",
"address1": "1 Example Way",
"city": "Testville",
"state": "CA",
"zip": "94000",
"country": "US"
},
"rate": {
"carrier": "USPS",
"token": "usps_priority"
},
"max_cost": 12.85
}
'import requests
url = "https://catchbackcards.com/api/partner/v1/shipments"
payload = {
"card_ids": ["0e94dc61-5a2f-4b1e-9c3d-7f8a1b2c3d4e"],
"address": {
"name": "Sam Rivera",
"address1": "1 Example Way",
"city": "Testville",
"state": "CA",
"zip": "94000",
"country": "US"
},
"rate": {
"carrier": "USPS",
"token": "usps_priority"
},
"max_cost": 12.85
}
headers = {
"Idempotency-Key": "<idempotency-key>",
"Authorization": "Bearer <token>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {
'Idempotency-Key': '<idempotency-key>',
Authorization: 'Bearer <token>',
'Content-Type': 'application/json'
},
body: JSON.stringify({
card_ids: ['0e94dc61-5a2f-4b1e-9c3d-7f8a1b2c3d4e'],
address: {
name: 'Sam Rivera',
address1: '1 Example Way',
city: 'Testville',
state: 'CA',
zip: '94000',
country: 'US'
},
rate: {carrier: 'USPS', token: 'usps_priority'},
max_cost: 12.85
})
};
fetch('https://catchbackcards.com/api/partner/v1/shipments', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://catchbackcards.com/api/partner/v1/shipments",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'card_ids' => [
'0e94dc61-5a2f-4b1e-9c3d-7f8a1b2c3d4e'
],
'address' => [
'name' => 'Sam Rivera',
'address1' => '1 Example Way',
'city' => 'Testville',
'state' => 'CA',
'zip' => '94000',
'country' => 'US'
],
'rate' => [
'carrier' => 'USPS',
'token' => 'usps_priority'
],
'max_cost' => 12.85
]),
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>",
"Content-Type: application/json",
"Idempotency-Key: <idempotency-key>"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://catchbackcards.com/api/partner/v1/shipments"
payload := strings.NewReader("{\n \"card_ids\": [\n \"0e94dc61-5a2f-4b1e-9c3d-7f8a1b2c3d4e\"\n ],\n \"address\": {\n \"name\": \"Sam Rivera\",\n \"address1\": \"1 Example Way\",\n \"city\": \"Testville\",\n \"state\": \"CA\",\n \"zip\": \"94000\",\n \"country\": \"US\"\n },\n \"rate\": {\n \"carrier\": \"USPS\",\n \"token\": \"usps_priority\"\n },\n \"max_cost\": 12.85\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("Idempotency-Key", "<idempotency-key>")
req.Header.Add("Authorization", "Bearer <token>")
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://catchbackcards.com/api/partner/v1/shipments")
.header("Idempotency-Key", "<idempotency-key>")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"card_ids\": [\n \"0e94dc61-5a2f-4b1e-9c3d-7f8a1b2c3d4e\"\n ],\n \"address\": {\n \"name\": \"Sam Rivera\",\n \"address1\": \"1 Example Way\",\n \"city\": \"Testville\",\n \"state\": \"CA\",\n \"zip\": \"94000\",\n \"country\": \"US\"\n },\n \"rate\": {\n \"carrier\": \"USPS\",\n \"token\": \"usps_priority\"\n },\n \"max_cost\": 12.85\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://catchbackcards.com/api/partner/v1/shipments")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Idempotency-Key"] = '<idempotency-key>'
request["Authorization"] = 'Bearer <token>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"card_ids\": [\n \"0e94dc61-5a2f-4b1e-9c3d-7f8a1b2c3d4e\"\n ],\n \"address\": {\n \"name\": \"Sam Rivera\",\n \"address1\": \"1 Example Way\",\n \"city\": \"Testville\",\n \"state\": \"CA\",\n \"zip\": \"94000\",\n \"country\": \"US\"\n },\n \"rate\": {\n \"carrier\": \"USPS\",\n \"token\": \"usps_priority\"\n },\n \"max_cost\": 12.85\n}"
response = http.request(request)
puts response.read_body{
"id": "7c1e0000-…",
"status": "processing",
"card_ids": [
"0e94dc61-…"
],
"carrier": "USPS",
"service": "Priority Mail",
"service_token": "usps_priority",
"cost": 12.85,
"cover_amount": null,
"insurance_cost": 0,
"destination_country": "US",
"tracking_number": null,
"tracking_url": null,
"created_at": "2026-11-02T18:04:11.000Z",
"status_updated_at": null,
"shipped_at": null,
"expected_delivery": null,
"delivered_at": null
}{
"error": "Insufficient balance",
"code": "INSUFFICIENT_BALANCE",
"amount": 12.85
}{
"error": "Missing or malformed API key"
}{
"error": "<string>",
"code": "FEATURE_RESTRICTED",
"capability": "<string>"
}{
"error": "The rate is now above your max_cost.",
"code": "PRICE_CHANGED",
"amount": 14.1,
"max_cost": 12.85
}{
"error": "<string>",
"code": "IDEMPOTENCY_KEY_REUSED"
}{
"error": "Daily shipment limit reached: 50 per key per UTC day.",
"code": "DAILY_SHIPMENT_LIMIT_REACHED",
"limit": 50,
"resets_at": "2026-11-03T00:00:00.000Z"
}{
"error": "We could not confirm whether this shipment went through. It is with our team; retrying with the same Idempotency-Key is safe and will not charge again.",
"code": "SHIPMENT_OUTCOME_UNKNOWN",
"shipment_id": "7c1e0000-…",
"needs_review": true
}{
"error": "<string>",
"code": "<string>"
}{
"error": "<string>",
"code": "<string>"
}Create a shipment
Scope: ship:cards, opt-in, granted on request (needs a linked account). Moves money.
Ships cards you hold to a customer’s address. It charges your balance the rate’s full amount, plus the premium if you choose insurance, and queues the shipment for us to fulfill. We quote again before charging: if the total has gone above max_cost, nothing is charged. The account needs a verified phone number, and a key can make 50 shipments per UTC day.
Use a new Idempotency-Key for each shipment, and resend the same key and body to retry it: a finished shipment comes back, and is never charged twice. A refused shipment charges nothing and frees its key.
SHIPMENT_OUTCOME_UNKNOWN means the charge may have gone through. Don’t resend it under a new key until you hear from us: what to do. Read Shipping cards first, including what covers a parcel.
curl --request POST \
--url https://catchbackcards.com/api/partner/v1/shipments \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--header 'Idempotency-Key: <idempotency-key>' \
--data '
{
"card_ids": [
"0e94dc61-5a2f-4b1e-9c3d-7f8a1b2c3d4e"
],
"address": {
"name": "Sam Rivera",
"address1": "1 Example Way",
"city": "Testville",
"state": "CA",
"zip": "94000",
"country": "US"
},
"rate": {
"carrier": "USPS",
"token": "usps_priority"
},
"max_cost": 12.85
}
'import requests
url = "https://catchbackcards.com/api/partner/v1/shipments"
payload = {
"card_ids": ["0e94dc61-5a2f-4b1e-9c3d-7f8a1b2c3d4e"],
"address": {
"name": "Sam Rivera",
"address1": "1 Example Way",
"city": "Testville",
"state": "CA",
"zip": "94000",
"country": "US"
},
"rate": {
"carrier": "USPS",
"token": "usps_priority"
},
"max_cost": 12.85
}
headers = {
"Idempotency-Key": "<idempotency-key>",
"Authorization": "Bearer <token>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {
'Idempotency-Key': '<idempotency-key>',
Authorization: 'Bearer <token>',
'Content-Type': 'application/json'
},
body: JSON.stringify({
card_ids: ['0e94dc61-5a2f-4b1e-9c3d-7f8a1b2c3d4e'],
address: {
name: 'Sam Rivera',
address1: '1 Example Way',
city: 'Testville',
state: 'CA',
zip: '94000',
country: 'US'
},
rate: {carrier: 'USPS', token: 'usps_priority'},
max_cost: 12.85
})
};
fetch('https://catchbackcards.com/api/partner/v1/shipments', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://catchbackcards.com/api/partner/v1/shipments",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'card_ids' => [
'0e94dc61-5a2f-4b1e-9c3d-7f8a1b2c3d4e'
],
'address' => [
'name' => 'Sam Rivera',
'address1' => '1 Example Way',
'city' => 'Testville',
'state' => 'CA',
'zip' => '94000',
'country' => 'US'
],
'rate' => [
'carrier' => 'USPS',
'token' => 'usps_priority'
],
'max_cost' => 12.85
]),
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>",
"Content-Type: application/json",
"Idempotency-Key: <idempotency-key>"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://catchbackcards.com/api/partner/v1/shipments"
payload := strings.NewReader("{\n \"card_ids\": [\n \"0e94dc61-5a2f-4b1e-9c3d-7f8a1b2c3d4e\"\n ],\n \"address\": {\n \"name\": \"Sam Rivera\",\n \"address1\": \"1 Example Way\",\n \"city\": \"Testville\",\n \"state\": \"CA\",\n \"zip\": \"94000\",\n \"country\": \"US\"\n },\n \"rate\": {\n \"carrier\": \"USPS\",\n \"token\": \"usps_priority\"\n },\n \"max_cost\": 12.85\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("Idempotency-Key", "<idempotency-key>")
req.Header.Add("Authorization", "Bearer <token>")
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://catchbackcards.com/api/partner/v1/shipments")
.header("Idempotency-Key", "<idempotency-key>")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"card_ids\": [\n \"0e94dc61-5a2f-4b1e-9c3d-7f8a1b2c3d4e\"\n ],\n \"address\": {\n \"name\": \"Sam Rivera\",\n \"address1\": \"1 Example Way\",\n \"city\": \"Testville\",\n \"state\": \"CA\",\n \"zip\": \"94000\",\n \"country\": \"US\"\n },\n \"rate\": {\n \"carrier\": \"USPS\",\n \"token\": \"usps_priority\"\n },\n \"max_cost\": 12.85\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://catchbackcards.com/api/partner/v1/shipments")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Idempotency-Key"] = '<idempotency-key>'
request["Authorization"] = 'Bearer <token>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"card_ids\": [\n \"0e94dc61-5a2f-4b1e-9c3d-7f8a1b2c3d4e\"\n ],\n \"address\": {\n \"name\": \"Sam Rivera\",\n \"address1\": \"1 Example Way\",\n \"city\": \"Testville\",\n \"state\": \"CA\",\n \"zip\": \"94000\",\n \"country\": \"US\"\n },\n \"rate\": {\n \"carrier\": \"USPS\",\n \"token\": \"usps_priority\"\n },\n \"max_cost\": 12.85\n}"
response = http.request(request)
puts response.read_body{
"id": "7c1e0000-…",
"status": "processing",
"card_ids": [
"0e94dc61-…"
],
"carrier": "USPS",
"service": "Priority Mail",
"service_token": "usps_priority",
"cost": 12.85,
"cover_amount": null,
"insurance_cost": 0,
"destination_country": "US",
"tracking_number": null,
"tracking_url": null,
"created_at": "2026-11-02T18:04:11.000Z",
"status_updated_at": null,
"shipped_at": null,
"expected_delivery": null,
"delivered_at": null
}{
"error": "Insufficient balance",
"code": "INSUFFICIENT_BALANCE",
"amount": 12.85
}{
"error": "Missing or malformed API key"
}{
"error": "<string>",
"code": "FEATURE_RESTRICTED",
"capability": "<string>"
}{
"error": "The rate is now above your max_cost.",
"code": "PRICE_CHANGED",
"amount": 14.1,
"max_cost": 12.85
}{
"error": "<string>",
"code": "IDEMPOTENCY_KEY_REUSED"
}{
"error": "Daily shipment limit reached: 50 per key per UTC day.",
"code": "DAILY_SHIPMENT_LIMIT_REACHED",
"limit": 50,
"resets_at": "2026-11-03T00:00:00.000Z"
}{
"error": "We could not confirm whether this shipment went through. It is with our team; retrying with the same Idempotency-Key is safe and will not charge again.",
"code": "SHIPMENT_OUTCOME_UNKNOWN",
"shipment_id": "7c1e0000-…",
"needs_review": true
}{
"error": "<string>",
"code": "<string>"
}{
"error": "<string>",
"code": "<string>"
}Authorizations
Your API key, sent as a bearer token. Keep it on your server. See Authentication. A request from a location we refuse gets a 403 with Access denied, whether or not it has a valid key: where you can call from.
Headers
Your id for this shipment, 8–128 characters of A-Za-z0-9_-, unique per account among your shipments. Pack and card order ids are a separate set.
^[A-Za-z0-9_-]{8,128}$Body
Card ids from Get vault, cards you hold in the vault. A listed card has to be delisted first.
1 - 10 elementsYour customer's address. Stored only with a shipment, never in logs or on your account.
Show child attributes
Show child attributes
The rate you chose from Estimate a shipment.
Show child attributes
Show child attributes
The most you'll pay for this shipment, in dollars, with any insurance. Normally the amount from the estimate, or its total if you chose insurance.
x <= 10000Optional insurance: the dollars of cover, with at most two decimals, from $100 up to max_cover from the estimate. Leave it out and the only cover is what the carrier includes automatically: up to $100 on USPS and UPS labels. Other carriers may include less or none. You carry any loss above your cover.
100 <= x <= 10000Response
The shipment. A new one is processing. A retry of one that went through returns it as it stands now, with idempotent_replay: true.
A shipment as a webhook sends it. It has nothing from the customer's address.
The shipment's id. Get a shipment takes it.
pending: the shipment hasn't reached an order yet.needs_review: we couldn't confirm the charge, and it's with our team.processing: the order is queued for fulfillment.shipped,delivered: the carrier has it, or has delivered it.cancelled: the order was cancelled or resolved outside shipping.
pending, needs_review, processing, shipped, delivered, cancelled null until we've confirmed a rate.
What the shipment charges in USD, including any insurance premium. null until we've confirmed a rate.
The insurance on this shipment, in USD. null without insurance, and before the order exists.
The premium in USD, which is part of cost. 0 without insurance, and null before the order exists.
null until there's a label.
When the order's status last changed.
Present and true while status is needs_review.
Present and true once we've removed the address.

