NextRare×Tria

Shipping

Physical redemption, as we run it on Collector Crypt Shipping API v1

Our server calls the shipping API with a server key; the user never signs in to the provider. The only thing the user signs is the redemption (the burn), with their own wallet. Our house wallet can pay the fee for them.

EnvironmentBase URL
Devhttps://dev-api.collectorcrypt.com/partner/v1
Prodhttps://api.collectorcrypt.com/partner/v1

Auth: Authorization: Bearer <key>. Errors: { "statusCode": 404, "code": "CUSTOMER_UNKNOWN", "message": "…" }; we branch on code. Costs are decimal USD strings.

1. Register the user

POST /customers

{ "externalId": "<our user id>", "walletChain": "solana", "wallet": "<base58>" }
→ { "externalId": "…", "walletChain": "solana", "wallet": "…", "created": true, "createdAt": "…" }

Idempotent. One externalId per wallet. GET /customers/{id} returns the same object.

2. Save an address

POST /customers/{id}/addresses

{ "fullName": "Ada Lovelace", "streetAddress": "1 Test St", "apartment": null,
  "city": "Billings", "state": "MT", "zip": "59102", "country": "US",
  "phoneNumber": "+14065550100", "email": "ada@example.com" }
→ { "id": "cm…", "state": "Montana", "country": "United States", … }

Also GET, PATCH, DELETE /customers/{id}/addresses/{addressId}. A shipment freezes its address at creation.

3. Estimate

POST /customers/{id}/shipments/estimate

{ "nftAddresses": ["<mint>"], "addressId": "cm…", "payCustomsDuties": false }
→ { "shippingPrice": "5.99", "insurancePrice": "0.00", "feesPrice": "0.00", "total": "5.99",
    "customsDutiesEstimate": null, "breakdown": { "lines": [ ] } }

4. Create the shipment

POST /customers/{id}/shipments

{ "nftAddresses": ["<mint>"], "addressId": "cm…", "payCustomsDuties": false,
  "payerWallet": "<house wallet>" }
→ { "outboundShipmentId": "cm…", "reused": false, "rail": "solana",
    "totalCost": "5.99", "amountDue": "5.99", "breakdown": { },
    "transactions": ["<base64>"], "delistTransactions": [] }

With payerWallet, the first transaction needs two signatures: the house wallet (fee) and the user (card owner). Transactions expire in about 60–90 seconds; call rebuild, never a new shipment. Posting the same body again returns the same shipment (reused: true).

5. Submit

POST /customers/{id}/shipments/{sid}/burn

{ "transactions": ["<base64 signed>"], "delistTransactions": [] }
→ [ { "error": null, "transactionId": "<signature>", "transactionUrl": "…" } ]

A non-null error means that transaction did not land. If the wallet sent them itself, report them with POST …/{sid}/signatures { "signatures": [ … ] } → linked / pending / rejected. POST …/{sid}/rebuild returns fresh transactions for unburned cards without charging the fee twice.

6. Track

GET /customers/{id}/shipments/{sid}

{ "id": "cm…", "customId": "2026100100OS123", "status": "Shipped", "nftAddresses": ["…"],
  "totalCost": "5.99", "paymentRail": "solana", "paymentConfirmedAt": "…",
  "trackingIds": ["1Z…"], "trackingUrls": ["https://…"], "updatedAt": "…" }

List: GET /customers/{id}/shipments?status=&page=&pageSize= (≤ 100).

Statuses (a set, never backwards): Created, PaymentPending, PaymentReceived, Pending, Processing, Shipped, Delivered, ActionRequired, Cancelled.

7. Webhook

POST to our URL on every status or tracking change:

{ "id": "<event id>", "type": "shipment.updated", "createdAt": "…",
  "data": { "customerExternalId": "…", "shipment": { } } }
  • data.shipment is exactly the GET shape: current state, not a diff.
  • At least once, possibly out of order: we de-duplicate by id and ignore anything older than shipment.updatedAt.
  • Signed: CC-Signature: t=<unix>,v1=<hex HMAC-SHA256(secret, "<t>.<raw body>")>, rejected if t is more than 5 minutes off.
  • Any 2xx within 10 seconds is success; failures retry for about 45 hours.

Errors we handle

StatusCodes
400VALIDATION_FAILED, CONTACT_PHONE_REQUIRED, CONTACT_EMAIL_REQUIRED, DESTINATION_RESTRICTED, WALLET_NOT_SUPPORTED, CARD_NOT_REDEEMABLE, INSUFFICIENT_BALANCE
401 / 403UNAUTHORIZED, SCOPE_MISSING, RAIL_NOT_ENABLED, BATCH_NOT_ISSUED (call rebuild), BATCH_INCOMPLETE
404CUSTOMER_UNKNOWN, ADDRESS_NOT_FOUND, SHIPMENT_NOT_FOUND, CARDS_NOT_FOUND
409CUSTOMER_WALLET_CONFLICT, WALLET_ALREADY_REGISTERED, SHIPMENT_NOT_MODIFIABLE, DELIST_FAILED
5xxINTERNAL, UNAVAILABLE: retry with backoff

What we need from you

The same shape is ideal: server-keyed, our own customer ids, the house wallet able to pay the fee, idempotent create, and a signed status webhook. Deadstock ships from Japan; tell us which countries you ship to and how duties are handled.

On this page