Partner API
Build carts from product URLs or live Collector Crypt cards, handle reviewed quotes as a normal asynchronous state, and pay server-authoritative orders in USDC.
Authentication and authority
Keep credentials and all partner API calls on your server.
X-API-Key: sp3nd_...
X-API-Secret: sp3nd_sec_...Base URLschema_versionorder_schema_versionpayment_readyOrder lifecycle
A review state is a successful order creation, not an API failure.
{
"status": "Created",
"checkout_role": "direct_payment",
"pricing_status": "ready_for_payment",
"requires_manual_quote": false,
"payment_ready": true
}{
"status": "Awaiting Review",
"checkout_role": "manual_review",
"pricing_status": "awaiting_team_quote",
"requires_manual_quote": true,
"payment_ready": false
}{
"checkout_role": "manual_review",
"pricing_status": "shipping_selection_required",
"payment_ready": false,
"selected_shipping_option_id": null
}If any item in a mixed cart needs manual review, the entire cart becomes one `Awaiting Review` order in v2. It is not split into child orders.
Collector Crypt carts are separate: one unique card, quantity `1`, never mixed with shipped products. After payment, `Paid` confirms SP3ND treasury receipt only—not provider purchase or wallet delivery. Approved Partners can search, quote, create, and pay card orders without a separate opt-in. SP3ND manually buys the exact card and transfers it to the buyer wallet.
checkout_rolepricing_statuspayment_readyCreate a server-priced cart
Send URLs or one Collector Crypt asset. SP3ND resolves product metadata and commerce values.
const response = await fetch(
'https://us-central1-sp3nddotshop-prod.cloudfunctions.net/createPartnerCart',
{
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-API-Key': process.env.SP3ND_API_KEY,
'X-API-Secret': process.env.SP3ND_API_SECRET
},
body: JSON.stringify({
ship_to_country: 'United States',
ship_to_postal_code: '10007',
items: [{
product_url: 'https://www.ebay.com/itm/123456789',
quantity: 1
}]
})
}
);Physical carts expire after 30 minutes. A complete `shipping_address` can replace the country/postal shortcut. Legacy clients can send extra item metadata, but caller-supplied titles and prices are not treated as verified data.
Search and quote Collector Crypt cards
Discovery is live, authenticated, and read-only; the cart refetch is the pricing authority.
GET https://us-central1-sp3nddotshop-prod.cloudfunctions.net/searchCollectorCryptCards?q=charizard%20psa%2010&limit=20
X-API-Key: <api-key>
X-API-Secret: <api-secret>
// response
{
"success": true,
"schema_version": 2,
"provider": "collectorcrypt",
"currency": "USDC",
"items": [{
"pricing_source": "collectorcrypt_live_listing",
"product_type": "tokenized_collectible",
"fulfillment_type": "wallet_delivery",
"card_id": "<provider-card-id>",
"nft_address": "<asset-address>",
"product_url": "https://collectorcrypt.com/assets/solana/<asset-address>",
"back_image_url": "https://<provider-image-host>/back.jpg",
"price": 125,
"listing_price_usdc": 125,
"currency": "USDC",
"card_type": "Pokemon",
"grading_id": "<provider-grading-id>",
"insured_value_usd": 150,
"listed_at": "2026-09-01T00:00:00.000Z",
"listing_updated_at": "2026-09-01T00:01:00.000Z",
"max_quantity": 1
}],
"next_cursor": null,
"total": 1,
"marketplace_total": 2400,
"total_pages": 1,
"category_counts": { "Pokemon": 1800, "Sports": 600 }
}qlimitcursorpageResults include only native Solana Collector Crypt V2 buy-now listings priced in USDC with a supported `token_standard` (`Pnft`, `Cnft`, `StandardNft`, or `CoreNft`) and an exact cent-denominated price. `listing_price_usdc` mirrors generic `price`; insured value and listing timestamps are catalog metadata. Upstream totals, page totals, and category counts are computed before SP3ND filters ineligible rows, so they may exceed `items.length`. Search does not reserve the listing. Create a five-minute, one-card cart from the canonical asset URL or from `{ user_wallet: buyerWallet, items: [{ provider: "collectorcrypt", nft_address, quantity: 1 }] }`. Omit shipping and destination fields; the card cannot be mixed with Amazon or eBay items. An optional top-level cart `user_wallet` must be a valid on-curve Solana address. SP3ND stores it and uses it when the order request omits its own `user_wallet`; final buyer/recipient equality is enforced at order creation. An invalid cart wallet returns `USER_WALLET_INVALID`.
Marketplace storefronts
Published URL support and destination delivery are separate checks.
Amazon
amazon.com, amazon.co.uk, amazon.ca, amazon.de, amazon.fr, amazon.es, amazon.it, amazon.nl, amazon.com.be, amazon.pl, amazon.se, amazon.com.br, amazon.com.mx, amazon.com.au, amazon.in, amazon.co.jp, amazon.sg, amazon.ae, amazon.sa, amazon.eg, amazon.com.tr, amazon.co.za, amazon.ie, amazon.be, amazon.cn
Shares: a.co, amzn.to, amzn.eu, amzn.asia
eBay
ebay.com, ebay.ca, ebay.co.uk, ebay.de, ebay.at, ebay.fr, ebay.it, ebay.es, ebay.ie, ebay.nl, ebay.be, ebay.ch, ebay.pl, ebay.com.au, ebay.com.hk, ebay.com.my, ebay.ph, ebay.com.sg, ebay.in, ebay.cn, ebay.com.tw, ebay.vn, ebay.co.th
Product shares: ebay.io/m/<code>
Collector Crypt
Exact canonical URL: collectorcrypt.com/assets/solana/<asset-address>
Native Solana V2 buy-now listings in USDC only. The tokenized asset is delivered to `asset_recipient_wallet`; no physical shipping address is used.
Storefront support does not guarantee that a particular seller will ship a listing to every destination. SP3ND checks the listing using the final country and postal code. A cross-storefront order is valid only when SP3ND receives a verified ship-to-home quote for that address. Prefer the recipient country's storefront when one is available. Share links are expanded and revalidated server-side; SP3ND stores the canonical marketplace product URL.
Create an idempotent order
One stable idempotency key represents one checkout attempt.
const response = await fetch(
'https://us-central1-sp3nddotshop-prod.cloudfunctions.net/createPartnerOrder',
{
method: 'POST',
headers: {
...authHeaders,
'Idempotency-Key': checkoutAttemptId
},
body: JSON.stringify({
cart_id: cart.cart_id,
customer_email: 'buyer@example.com',
shipping_address: {
name: 'Ada Buyer',
recipient: 'Ada Buyer',
address1: '123 Main Street',
address2: 'Suite 4',
city: 'New York',
state: 'NY',
postalCode: '10007',
country: 'United States',
phone: '+12125550123'
}
})
}
);Collector Crypt order
const response = await fetch(
'https://us-central1-sp3nddotshop-prod.cloudfunctions.net/createPartnerOrder',
{
method: 'POST',
headers: {
...authHeaders,
'Idempotency-Key': cardCheckoutAttemptId
},
body: JSON.stringify({
cart_id: cardCart.cart_id,
customer_email: 'buyer@example.com',
user_wallet: buyerWallet,
asset_recipient_wallet: buyerWallet
})
}
);Omit `shipping_address` for a Collector Crypt-only cart. Send the same valid, on-curve buyer address as `user_wallet` and `asset_recipient_wallet`. The server copies one to the other when a field is omitted, but new integrations should send both identically. Gifting is not supported; a mismatch returns `ASSET_RECIPIENT_WALLET_MISMATCH`. The wallet is frozen into the idempotent order and cannot change after payment preparation begins.
Idempotency-Keysame key + same requestsame key + changed requestReview quotes and shipping
Poll the order, present current choices, and submit an opaque option ID.
GET https://us-central1-sp3nddotshop-prod.cloudfunctions.net/getPartnerOrder?order_id=abc123
POST https://us-central1-sp3nddotshop-prod.cloudfunctions.net/selectPartnerOrderShippingOption
{
"order_id": "abc123",
"shipping_option_id": "standard"
}quote_revisionquote_expires_atshipping_optionsselected_shipping_option_idselected_shipping_optionselected_manual_shipping_optionQuote revision and expiry are enforced on selection. If the server returns a conflict, refresh the order and show the current options. Never calculate or patch the total client-side.
Pay a ready order
Shipped products default to createPartnerTransaction; Collector Crypt uses exact partnerPayment prepare/submit bytes.
If you were issued Partner API credentials for a normal server integration, use `createPartnerTransaction`. Do not use `payAgentOrder`. For shipped products, call the standard endpoint once, then build and submit the on-chain payment in your own wallet integration using only authoritative SP3ND values. Collector Crypt is the explicit exception below.
Never call `createPartnerTransaction` and then `payAgentOrder` for the same checkout. They are two different payment paths and must not share client-side settlement state.
Shipped-product Partner API flow
POST https://us-central1-sp3nddotshop-prod.cloudfunctions.net/createPartnerTransaction
Content-Type: application/json
X-API-Key: <api-key>
X-API-Secret: <api-secret>
{
"order_id": "abc123",
"sender_address": "<buyer-solana-wallet>"
}
// 201 response: use these exact server-derived fields
{
"success": true,
"status": "pending",
"order_id": "abc123",
"order_number": "ORD-...",
"amount": 10.81,
"currency": "USDC",
"memo": "SP3ND Order: ORD-...",
"recipient_address": "<sp3nd-treasury>"
}`createPartnerTransaction` registers the authoritative payment attempt; it does not submit or sign a wallet transaction for you. Make that authenticated request on your server. Pass only its returned payment instructions to your trusted wallet signer. It rejects real Collector Crypt orders with `COLLECTOR_CRYPT_PAYMENT_METHOD_UNSUPPORTED`.
Exact Solana USDC construction
import {
PublicKey,
Transaction,
TransactionInstruction,
} from '@solana/web3.js';
import {
ASSOCIATED_TOKEN_PROGRAM_ID,
TOKEN_PROGRAM_ID,
createTransferCheckedInstruction,
getAssociatedTokenAddressSync,
} from '@solana/spl-token';
const USDC_MINT = new PublicKey(
'EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v'
);
const MEMO_PROGRAM_ID = new PublicKey(
'MemoSq4gqABAXKb96qnH8TysNcWxMyWCqXgDLGmfcHr'
);
// 10.81 USDC -> 10_810_000n. Never multiply a JS number by 1e6.
function toUsdcAtomicUnits(decimalAmount) {
const match = /^(0|[1-9]\d*)(?:\.(\d{1,6}))?$/.exec(
String(decimalAmount)
);
if (!match) throw new Error('Invalid USDC amount from SP3ND');
const [, whole, fraction = ''] = match;
return BigInt(whole) * 1_000_000n +
BigInt(fraction.padEnd(6, '0'));
}
async function signAndBroadcastSp3ndPayment({
payment,
buyerPublicKey,
connection,
signTransaction,
}) {
if (payment.status === 'confirmed') return null; // Never pay again.
if (payment.status !== 'pending' || payment.currency !== 'USDC') {
throw new Error('SP3ND payment is not payable');
}
const recipientOwner = new PublicKey(payment.recipient_address);
const buyerUsdcAta = getAssociatedTokenAddressSync(
USDC_MINT, buyerPublicKey, false,
TOKEN_PROGRAM_ID, ASSOCIATED_TOKEN_PROGRAM_ID
);
const recipientUsdcAta = getAssociatedTokenAddressSync(
USDC_MINT, recipientOwner, false,
TOKEN_PROGRAM_ID, ASSOCIATED_TOKEN_PROGRAM_ID
);
const { blockhash, lastValidBlockHeight } =
await connection.getLatestBlockhash('confirmed');
const transaction = new Transaction({
feePayer: buyerPublicKey,
recentBlockhash: blockhash,
}).add(
createTransferCheckedInstruction(
buyerUsdcAta,
USDC_MINT,
recipientUsdcAta,
buyerPublicKey,
toUsdcAtomicUnits(payment.amount),
6,
[],
TOKEN_PROGRAM_ID
),
new TransactionInstruction({
programId: MEMO_PROGRAM_ID,
keys: [],
data: Buffer.from(payment.memo, 'utf8'),
})
);
const signed = await signTransaction(transaction); // buyer signs
const signature = await connection.sendRawTransaction(signed.serialize());
await connection.confirmTransaction(
{ signature, blockhash, lastValidBlockHeight },
'confirmed'
);
return signature;
}- Use the returned amount, currency, memo, and recipient unchanged. Convert the decimal amount by padding to six places: `10.81` becomes exactly `10,810,000` atomic units, never a floating-point multiplication.
- Treat `recipient_address` as the treasury owner. Derive both canonical ATAs from the Solana mainnet USDC mint and the standard Token/Associated Token programs.
- Fetch a fresh blockhash and build a legacy `Transaction` with the buyer as fee payer and `TransferChecked` authority. Add `TransferChecked` followed by the Memo Program instruction using the exact memo bytes.
- Sign and broadcast through the partner's wallet and Solana RPC. Do not send `PAYMENT-SIGNATURE` or call a facilitator.
- Poll `getPartnerOrder` until SP3ND confirms `Paid`. Never send a second payment while confirmation is pending.
HTTP `200` with `status: confirmed` means the order was already paid and no transaction may be constructed. HTTP `201` with `status: pending` supplies the one payment above. On `409` or any other non-2xx response, do not broadcast; read the order and follow its lifecycle code.
Collector Crypt: exact prepare/submit flow
POST https://us-central1-sp3nddotshop-prod.cloudfunctions.net/partnerPayment
Content-Type: application/json
X-API-Key: <api-key>
X-API-Secret: <api-secret>
{
"action": "prepare",
"order_id": "abc123",
"payer_address": "<buyer-solana-wallet>"
}
// 201 new preparation; a safe replay is 200 + idempotent_replay=true
{
"success": true,
"status": "payment_prepared",
"idempotent_replay": false,
"order_id": "abc123",
"order_number": "ORD-...",
"amount": 125,
"currency": "USDC",
"network": "solana-mainnet",
"payer_address": "<buyer-solana-wallet>",
"recipient_address": "<sp3nd-treasury>",
"token_mint": "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v",
"token_decimals": 6,
"amount_atomic": "125000000",
"memo": "SP3ND Order: ORD-...",
"unsigned_transaction_base64": "<exact-unsigned-transaction>",
"recent_blockhash": "<blockhash>",
"last_valid_block_height": 123456789
}Verify the displayed payment fields, then have exactly `payer_address` sign the exact returned transaction. Do not rebuild it, change instructions, replace the blockhash, or sign a second authorization. `PAYMENT_TRANSACTION_NEAR_EXPIRY` means do not sign; wait until the stated blockheight passes before preparing again.
POST https://us-central1-sp3nddotshop-prod.cloudfunctions.net/partnerPayment
Content-Type: application/json
X-API-Key: <api-key>
X-API-Secret: <api-secret>
{
"action": "submit",
"order_id": "abc123",
"signed_transaction_base64": "<exact-wallet-signed-transaction>"
}
// 202 response
{
"success": true,
"status": "verifying_payment",
"idempotent_replay": false,
"order_id": "abc123",
"order_number": "ORD-...",
"transaction_signature": "<solana-signature>"
}A prepare retry for the same unchanged order and payer safely returns the same valid bytes. A submit retry must reuse the exact same `signed_transaction_base64`. After a timeout, network error, or `PAYMENT_BROADCAST_UNKNOWN`, read the order first and never rebuild, re-sign, or prepare a replacement while the original outcome is unknown. `verifying_payment` is not `Paid`.
Use `payAgentOrder` only if your client intentionally implements SP3ND's published legacy x402 agentic-skill contract. It is not the next step after `createPartnerTransaction`, and it is not the MCP payment path. Real Collector Crypt orders are rejected on this surface and use `partnerPayment` only. Follow the separate published SP3ND agentic skill or contact SP3ND for the current legacy integration contract.
For these paths, `order_number` is optional and validated when sent. Caller-provided `amount`, `currency`, `memo`, and `recipient_address` are ignored. Payment can begin only when the latest response has `payment_ready: true`; `pricing_status` is informational and is never a second payment gate.
Supported endpoints
Cloud Function endpoint names are part of the public contract.
searchCollectorCryptCards?q=...&limit=...&cursor=...&page=...Search live Collector Crypt USDC cardscreatePartnerCartCreate a server-priced cartaddItemToPartnerCart/{cartId}Add one itemupdateCartItemQuantity/{cartId}/{itemId}Change quantityremoveCartItem/{cartId}/{itemId}Remove an itemupdateCartShippingAddress/{cartId}Change destination and repricegetPartnerCart/{cartId}Read a cartcreatePartnerOrderCreate an ordergetPartnerOrder?order_id=...Read one ordergetPartnerOrdersList ordersgetPartnerKpis?start_date=...&end_date=...Read aggregate-only KPIsselectPartnerOrderShippingOptionChoose reviewed shippingcreatePartnerTransactionShipped-product payment attempt; rejects Collector CryptpartnerPaymentPrepare or submit exact Collector Crypt payment bytespayAgentOrderAdvanced legacy agentic-skill clients onlyTo list all orders, omit `status` or use `status=all`. The plural order endpoint accepts integer `limit` (`1..100`, default `50`) and `offset` (`0..499`, default `0`), with `limit + offset <= 500`. Optional `user_wallet` and `customer_email` filters are applied before pagination. The plural `addItemsToPartnerCart` endpoint is not supported in v2.
Aggregate reporting
Read current partner KPIs without receiving or storing customer order rows.
GET https://us-central1-sp3nddotshop-prod.cloudfunctions.net/getPartnerKpis?start_date=2026-08-01T00:00:00.000Z&end_date=2026-09-01T00:00:00.000Z
X-API-Key: <api-key>
X-API-Secret: <api-secret>`start_date` is inclusive and `end_date` is exclusive. Supply both as ISO-8601 timestamps with explicit timezones. Omit both for the trailing 30 days; the maximum range is 90 days. A request may scan at most 5,000 orders. A larger scan fails with HTTP `413` and `KPI_SCAN_LIMIT_EXCEEDED`; partial results are never returned.
The response separates `gross_created` from `settled` volume. `gross_created` includes every non-test order created in the range, including later cancelled or refunded orders. Fees and revshare are counted only in `settled`. Current `cancelled` and `refunded` buckets report count and gross order value, not actual loss or refund amounts. The status breakdown remains available. No order IDs, items, wallets, emails, addresses, or payment identifiers are returned.
Lifecycle conflicts
Use machine-readable codes and refresh state after quote conflicts.
ORDER_NOT_PAYMENT_READYPoll the order; do not submit payment.
ORDER_NOT_PAYABLEStop; this order cannot accept payment.
PAYMENT_SETTLEMENT_IN_PROGRESSDo not resubmit payment; poll the order.
PAYMENT_SETTLEMENT_UNKNOWNDo not retry; retain the order ID for manual reconciliation.
IDEMPOTENCY_CONFLICTUse the original payload or a new checkout key.
COLLECTOR_CRYPT_SINGLE_ITEM_REQUIREDCreate a one-card cart and do not mix it with shipped products.
COLLECTOR_CRYPT_QUERY_INVALID / COLLECTOR_CRYPT_LIMIT_INVALID / COLLECTOR_CRYPT_CURSOR_INVALID / COLLECTOR_CRYPT_PAGE_INVALIDFix the discovery input; pass an upstream cursor back unchanged.
USER_WALLET_INVALIDUse a valid on-curve Solana address for the optional cart user_wallet.
COLLECTOR_CRYPT_LISTING_CHANGEDCreate a fresh cart from the current live listing.
COLLECTOR_CRYPT_LISTING_UNAVAILABLEStop checkout; the card is no longer an eligible listing.
COLLECTOR_CRYPT_LISTING_IDENTITY_INVALIDStop; the listing lacks an immutable asset/receipt identity.
COLLECTOR_CRYPT_LISTING_RESERVEDStop; another SP3ND order owns this exact listing receipt.
COLLECTOR_CRYPT_ORDER_CHANGED / COLLECTOR_CRYPT_RESERVATION_EXPIREDDo not pay; read or rebuild the card order as directed.
COLLECTOR_CRYPT_PAYMENT_METHOD_UNSUPPORTEDUse partnerPayment; both legacy payment endpoints reject real cards.
ASSET_RECIPIENT_WALLET_MISMATCHUse the same wallet for user_wallet and asset_recipient_wallet; gifting is unavailable.
PAYER_ADDRESS_INVALID / PAYER_ADDRESS_MISMATCHPrepare with the valid buyer wallet frozen on the order.
ORDER_ID_REQUIRED / ASSET_RECIPIENT_WALLET_REQUIREDSend the opaque order ID and the same valid on-curve buyer/delivery wallet.
SIGNED_TRANSACTION_INVALID / SIGNED_TRANSACTION_REJECTEDSubmit only the bounded exact signed serialization returned by prepare.
PAYMENT_NOT_PREPARED / PAYMENT_ATTEMPT_EXISTS / PAYMENT_ATTEMPT_INVALIDRead the current order and attempt; never create a parallel payment.
ORDER_CHANGED / PAYMENT_TOTAL_INVALID / PAYMENT_AMOUNT_INVALIDStop and read the authoritative order; do not construct replacement payment values.
PAYMENT_SIGNATURE_CONFLICT / PAYMENT_SIGNATURE_REUSEDStop; never attach another signature or reuse one across orders.
COLLECTOR_CRYPT_PRICING_CONFIGURATION_INVALIDStop; SP3ND cannot create a card order whose total is below provider cost.
TEST_PAYMENT_UNSUPPORTEDDo not send a real wallet payment for a test order.
PAYMENT_TRANSACTION_NEAR_EXPIRYDo not sign; wait past the last-valid blockheight before preparing again.
PAYMENT_TRANSACTION_FAILED / PAYMENT_TRANSACTION_EXPIREDThe old attempt was proved terminal and released; prepare fresh bytes.
PAYMENT_STATUS_UNAVAILABLE / PAYMENT_PREFLIGHT_UNAVAILABLEPreserve and retry only the exact signed bytes when retryable.
PAYMENT_RECONCILIATION_PENDINGKeep the payment locked and retry only the original signed bytes.
QUOTE_EXPIREDFetch the current order and quote.
QUOTE_REVISION_MISMATCHRefresh before selecting shipping.
CART_EXPIREDCreate and price a new cart.
EBAY_UNDELIVERABLEStop for this listing and destination. A local-pickup-only listing is not retryable.
EBAY_QUOTE_UNAVAILABLEThe quote was temporary or inconclusive; retry later.
RATE_LIMITEDBack off before retrying.
Migration checklist
Support
Email support@sp3nd.shop with your partner name, endpoint, timestamp, and `order_id` or `cart_id`. Never send an API secret or signed payment payload.