Developers/Get started

Conventions

Rules that apply to all seven endpoints.

Related: Authentication · Catalog and orders · README

#Requests

  • Base URL: https://b2b.cardv.net/api/v1 (Live) or https://sandbox.cardv.net/api/v1 (Sandbox).
  • Paths have no trailing slash. Use /api/v1/orders, not /api/v1/orders/.
  • Send JSON bodies as UTF-8 with Content-Type: application/json.
  • Send amounts as strings, for example "9.2500". This avoids rounding errors.
  • Set a clear User-Agent, for example AcmeShop-CardV/1.4.

#Money and time

  • Money is a string with 4 decimals, for example "merchant_price": "9.2500".
  • Read it with a decimal type, never a floating-point number.
  • You pay in your wallet currency (default_currency in GET/account, currently USD).
  • face_currency is the currency printed on the card. It can differ from your wallet currency.
  • Always use merchant_price for your costs. Labels such as price_label are for display only.
  • All times are UTC in ISO 8601, for example 2026-09-29T08:15:30.123456Z.
  • Use a real ISO 8601 parser. The number of decimals in seconds can vary.
  • X-Timestamp for signing is Unix time in seconds.

#Identifiers

ThingExampleNotes
Merchant IDM00000001Never changes.
SKU IDS000456Use it to quote and order.
Product IDP000123The product a SKU belongs to.
CardV order IDO-00001234Use it to read an order.
Your order numberSHOP-10001external_order_id, 1–120 characters, unique.
  • Store IDs as text. Do not parse them. They may get longer.
  • Orders also have a numeric id. Do not use it. Use order_id.
  • For your order number, use only A–Z a–z 0–9 - _ ..

#Paging

Only GET/skus is paged. Send limit and offset:

HTTP
GET /api/v1/skus?limit=100&offset=200
JSON
{"count": 1234, "limit": 100, "results": ["..."], "filter_options": {}}
  • limit defaults to 100. The maximum is 500. Larger values are reduced to 500.
  • count is the total number of matches. Keep going until offset reaches count.
  • A negative or non-number limit or offset returns HTTP 400.
  • An unknown filter value returns an empty list, not an error.

#Rate limit

  • The default is 60 requests per minute for your whole account. All your keys and Portal users share it. Your tier may set a different number.

  • The minute starts at :00 on the clock. Rejected requests also count.

  • Over the limit, you get HTTP 429 and a Retry-After header (seconds to wait):

    JSON
    {"detail": "Merchant API rate limit exceeded. Expected available in 23 seconds."}
  • To stay under it: cache the SKU list, use webhooks instead of fast polling, and wait a little longer after each 429.

#Errors

Always check the HTTP status first. Then read the JSON body. The key in the body tells you what went wrong. Do not rely on the message text.

Authentication, permission, not-found and rate-limit errors use detail:

JSON
{"detail": "Order not found."}

Order and quote errors name the field:

JSON
{"balance": "Insufficient available balance."}

A badly formed order line is reported per line, in the same position as your items:

JSON
{"items": [{}, {"quantity": ["Ensure this value is greater than or equal to 1."]}]}
KeyWhereWhat to do
detailAnySee the status code below.
itemsPOST/ordersFix the line. If the price changed, quote again.
balancePOST/ordersAdd funds in the Portal.
riskPOST/ordersYou hit an order limit. Contact CardV.
external_order_idPOST/ordersOrder number missing, too long or used for another order.
walletPOST/ordersNo active wallet. Contact CardV.
quantity, amountQuoteOut of range or not a number.
limit, offsetGET/skusNot a valid number.

A few errors are not JSON:

  • HTTP 403 with plain text such as error code: 1010 comes from CardV's network edge. Your request never reached CardV. Send CardV your server IP and User-Agent.
  • An unknown path (404) or a proxy error (5xx) can return HTML.

Never log API keys, signatures or codes when you log errors.

#HTTP status codes

StatusMeaningRetry?
200Success. On POST/orders: the order already existed.No need
201A new order was created.No need
400The request was rejected. Nothing was charged.After fixing it
403Credentials, signature, IP, or a Portal-only endpoint.After fixing it
404Not found, or not open to your account.No
405Wrong method for this path.No
429Too many requests.After Retry-After
5xx or timeoutServer or network problem. The order may exist.Yes, see below

For POST/orders, retry only with the same body and order number. See the safe retry flow.

#Responses

  • brand_logo_url and image_url are full URLs to images hosted by CardV, or "". They are public and can be cached.
  • The order fields invoice_url and delivery_file_url are paths such as /orders/O-00001234/invoice, or "" when there is nothing yet. They are Portal only: with an API key they return HTTP 403. Open invoices and code CSV files in the Portal.

#Compatibility

  • Ignore fields you do not know. CardV adds fields without a new API version.
  • New status values may appear. Treat an unknown status as "not finished yet".
  • Do not depend on the order of JSON keys or the wording of messages.

Questions about your integration? Email [email protected] with your Merchant ID and the order or request ID.