{"openapi":"3.0.3","info":{"title":"giftcardshop.org Partner API","version":"1.0.0","description":"Crypto-only gift card aggregator API for B2B resellers.\n\n## Authentication\n\nEvery request requires an `Authorization: Bearer <api_key>` header.\nKeys are issued from the admin dashboard in the format\n`gcs_<env>_<prefix><secret>` where env is `live` or `test`. The key\nis shown once at creation; only the SHA-256 hash + 8-char prefix are\nstored server-side. Lose it -> mint a new one and revoke the old.\n\n## Idempotency\n\nMutating endpoints accept an `Idempotency-Key` header. Replays with\nthe same key on the same partner return the original response (status\nand body) verbatim - including error responses. Use a UUID per logical\norder; never reuse a key across distinct intents.\n\n## Pricing\n\nPrices in the catalogue are the buyer-facing prices (vendor cost +\nyour tier markup). Markup is set per (vendor, category, brand, tier)\nin the admin UI; the `markupBps` field on each variant tells you the\neffective markup applied. The authoritative price is recomputed at\norder-placement time - preview prices on GET /products/:id may be\nstale by seconds if the operator rotates rules mid-session.\n\n## Balance + KYC\n\nOrders debit your USD balance. Top up via POST /deposits (BTCPay\ninvoice -> settle on-chain -> ledger credit). Orders above the global\nKYC threshold are blocked with 402 + a verification URL until your\naccount passes verification.","contact":{"name":"giftcardshop.org","url":"https://giftcardshop.org"},"license":{"name":"Proprietary"}},"servers":[{"url":"https://api.giftcardshop.org","description":"production"}],"security":[{"bearerAuth":[]}],"tags":[{"name":"Account","description":"Identity + balance"},{"name":"Catalogue","description":"Browsable product catalogue with partner-tier prices"},{"name":"Deposits","description":"Top up the partner USD balance via crypto"},{"name":"Orders","description":"Place + track gift-card orders"}],"paths":{"/v1/partner/me":{"get":{"tags":["Account"],"summary":"Whoami","description":"Returns the partner identity tied to the bearer key plus the current USD balance.","responses":{"200":{"description":"Partner identity","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MeResponse"},"example":{"data":{"id":"prt_abc123","status":"active","kycStatus":"verified","tier":"gold","env":"live","balance":"12450.00"}}}}},"401":{"$ref":"#/components/responses/Unauthorized"}}}},"/v1/partner/balance":{"get":{"tags":["Account"],"summary":"Current USD balance","responses":{"200":{"description":"Balance snapshot","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BalanceResponse"},"example":{"data":{"balance":"12450.00","currency":"USD"}}}}},"401":{"$ref":"#/components/responses/Unauthorized"}}}},"/v1/partner/products":{"get":{"tags":["Catalogue"],"summary":"List products","description":"Paginated. Filter by `q` (name substring), `brand` (slug), `category` (slug), `country` (ISO-2).","parameters":[{"$ref":"#/components/parameters/Limit"},{"$ref":"#/components/parameters/Offset"},{"name":"q","in":"query","schema":{"type":"string","minLength":1,"maxLength":80},"description":"Case-insensitive substring match on product name."},{"name":"brand","in":"query","schema":{"type":"string","minLength":1,"maxLength":80}},{"name":"category","in":"query","schema":{"type":"string","minLength":1,"maxLength":80}},{"name":"country","in":"query","schema":{"type":"string","minLength":2,"maxLength":2},"description":"ISO 3166-1 alpha-2."}],"responses":{"200":{"description":"Page of products","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProductsPage"}}}},"401":{"$ref":"#/components/responses/Unauthorized"}}}},"/v1/partner/products/{id}":{"get":{"tags":["Catalogue"],"summary":"Get product with priced variants","parameters":[{"$ref":"#/components/parameters/ProductId"}],"responses":{"200":{"description":"Product detail with variants priced for this partner tier","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProductDetailResponse"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"}}}},"/v1/partner/deposits":{"get":{"tags":["Deposits"],"summary":"List deposits","parameters":[{"$ref":"#/components/parameters/Limit"},{"$ref":"#/components/parameters/Offset"}],"responses":{"200":{"description":"Page of deposits","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DepositsPage"}}}},"401":{"$ref":"#/components/responses/Unauthorized"}}},"post":{"tags":["Deposits"],"summary":"Open a top-up invoice","description":"Creates a BTCPay invoice for the requested amount/currency. Pay on any chain BTCPay supports; on settlement the balance is credited automatically. Poll GET /deposits/{id} (or wait for the partner webhook once configured) for status.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateDepositBody"},"example":{"amount":"1000.00","currency":"USD"}}}},"responses":{"201":{"description":"Deposit opened","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateDepositResponse"},"example":{"data":{"id":"dep_abc123","status":"pending","amount":"1000.00","currency":"USD","checkoutUrl":"https://btcpay.example.com/i/xyz","btcpayInvoiceId":"xyz","expiresAt":"2026-05-14T18:00:00.000Z"}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"}}}},"/v1/partner/deposits/{id}":{"get":{"tags":["Deposits"],"summary":"Get a deposit","parameters":[{"$ref":"#/components/parameters/DepositId"}],"responses":{"200":{"description":"Deposit row","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/Deposit"}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"}}}},"/v1/partner/orders":{"get":{"tags":["Orders"],"summary":"List orders","parameters":[{"$ref":"#/components/parameters/Limit"},{"$ref":"#/components/parameters/Offset"}],"responses":{"200":{"description":"Page of orders","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OrdersPage"}}}},"401":{"$ref":"#/components/responses/Unauthorized"}}},"post":{"tags":["Orders"],"summary":"Place an order","description":"Debits the partner balance, creates the order, and runs vendor fulfillment inline. Returns the order with delivered items on success.\n\nThe `Idempotency-Key` header is required - generate a fresh UUID per logical order, then retry the same key on network errors to safely deduplicate.\n\nFor `phone_refill` products (mobile top-ups), `recipientPhone` is mandatory in E.164 format. For other products it is ignored.","parameters":[{"$ref":"#/components/parameters/IdempotencyKey"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateOrderBody"},"example":{"productId":"prd_abc123","variantId":"pv_def456","email":"buyer@example.com","firstName":"Jane","lastName":"Doe","quantity":1}}}},"responses":{"200":{"description":"Order placed + fulfilled","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/OrderDetail"}}}}}},"400":{"description":"Bad request (missing idempotency key, recipient_phone_required, etc.)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"missingIdempotency":{"value":{"error":"missing_idempotency_key"}},"phoneRequired":{"value":{"error":"recipient_phone_required"}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"402":{"description":"Payment required: KYC blocked or insufficient balance","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"kycRequired":{"value":{"error":"kyc_required","thresholdUsd":"500.00","verificationUrl":"https://kyc.example.com/start?partner=prt_abc"}},"insufficientBalance":{"value":{"error":"insufficient_balance","balance":"10.00","required":"25.00","currency":"USD"}}}}}},"404":{"$ref":"#/components/responses/NotFound"}}}},"/v1/partner/orders/{id}":{"get":{"tags":["Orders"],"summary":"Get an order with items","parameters":[{"$ref":"#/components/parameters/OrderId"}],"responses":{"200":{"description":"Order with delivered items (code/pin populated when fulfilled)","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/OrderDetail"}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"}}}}},"components":{"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"gcs_<env>_<prefix><secret>","description":"API key minted in the admin dashboard. Format: `gcs_live_<8 chars><32 chars>` or `gcs_test_...`. Send as `Authorization: Bearer <key>`."}},"parameters":{"Limit":{"name":"limit","in":"query","schema":{"type":"integer","minimum":1,"maximum":100,"default":24}},"Offset":{"name":"offset","in":"query","schema":{"type":"integer","minimum":0,"default":0}},"ProductId":{"name":"id","in":"path","required":true,"schema":{"type":"string"},"description":"Product id, e.g. `prd_abc123`"},"OrderId":{"name":"id","in":"path","required":true,"schema":{"type":"string"},"description":"Order id, e.g. `ord_abc123`"},"DepositId":{"name":"id","in":"path","required":true,"schema":{"type":"string"},"description":"Deposit id, e.g. `dep_abc123`"},"IdempotencyKey":{"name":"Idempotency-Key","in":"header","required":true,"schema":{"type":"string","maxLength":200},"description":"Client-generated UUID. Replay-safe; repeating a key returns the original response verbatim."}},"responses":{"Unauthorized":{"description":"Missing, invalid, or revoked API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"unauthorized"}}}},"NotFound":{"description":"Resource does not exist (or does not belong to this partner)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"not_found"}}}},"BadRequest":{"description":"Validation failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"schemas":{"Error":{"type":"object","required":["error"],"properties":{"error":{"type":"string","description":"Machine-readable error code"}},"additionalProperties":true},"Money":{"type":"object","required":["amount","currency"],"properties":{"amount":{"type":"string","description":"Decimal string, e.g. \"100.00\". Currency-precision preserved."},"currency":{"type":"string","minLength":3,"maxLength":3,"example":"USD"}}},"MeResponse":{"type":"object","properties":{"data":{"type":"object","properties":{"id":{"type":"string","example":"prt_abc123"},"status":{"type":"string","enum":["pending","active","suspended","closed"]},"kycStatus":{"type":"string","enum":["unverified","pending","verified","rejected"]},"tier":{"type":"string","nullable":true,"example":"gold"},"env":{"type":"string","enum":["live","test"]},"balance":{"type":"string","example":"12450.00"}}}}},"BalanceResponse":{"type":"object","properties":{"data":{"type":"object","properties":{"balance":{"type":"string"},"currency":{"type":"string","example":"USD"}}}}},"ProductRow":{"type":"object","properties":{"id":{"type":"string","example":"prd_abc123"},"name":{"type":"string"},"imageUrl":{"type":"string","nullable":true,"format":"uri"},"brandId":{"type":"string","nullable":true},"categoryId":{"type":"string","nullable":true},"vendorId":{"type":"string","enum":["bitrefill","globetopper","reloadly","egifter"]},"deliveryType":{"type":"string","enum":["code","pin","redemption_url","file","phone_refill"]}}},"ProductsPage":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/ProductRow"}},"total":{"type":"integer"},"limit":{"type":"integer"},"offset":{"type":"integer"}}},"Variant":{"type":"object","properties":{"id":{"type":"string","example":"pv_def456"},"productId":{"type":"string"},"amount":{"type":"string","description":"Face value. For variable-amount cards this is the minimum."},"currency":{"type":"string","minLength":3,"maxLength":3},"isVariable":{"type":"string","nullable":true,"description":"\"true\" when buyer chooses any amount within [minAmount, maxAmount]."},"minAmount":{"type":"string","nullable":true},"maxAmount":{"type":"string","nullable":true},"available":{"type":"string","enum":["in_stock","out_of_stock","unknown"]},"partnerPrice":{"type":"string","description":"Buyer-facing price for this partner tier (face x (1 + markupBps/10000))."},"markupBps":{"type":"integer","description":"Markup in basis points (100 bps = 1%)."}}},"ProductDetailResponse":{"type":"object","properties":{"data":{"allOf":[{"$ref":"#/components/schemas/ProductRow"},{"type":"object","properties":{"description":{"type":"string","nullable":true},"termsUrl":{"type":"string","nullable":true,"format":"uri"},"variants":{"type":"array","items":{"$ref":"#/components/schemas/Variant"}},"countries":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","minLength":2,"maxLength":2},"name":{"type":"string"}}}}}}]}}},"Deposit":{"type":"object","properties":{"id":{"type":"string","example":"dep_abc123"},"amount":{"type":"string"},"currency":{"type":"string"},"status":{"type":"string","enum":["pending","paid","expired","failed"]},"btcpayInvoiceUrl":{"type":"string","nullable":true,"format":"uri"},"expiresAt":{"type":"string","nullable":true,"format":"date-time"},"paidAt":{"type":"string","nullable":true,"format":"date-time"},"createdAt":{"type":"string","format":"date-time"}}},"DepositsPage":{"type":"object","properties":{"data":{"type":"object","properties":{"rows":{"type":"array","items":{"$ref":"#/components/schemas/Deposit"}},"total":{"type":"integer"},"limit":{"type":"integer"},"offset":{"type":"integer"}}}}},"CreateDepositBody":{"type":"object","required":["amount"],"properties":{"amount":{"type":"string","pattern":"^\\d+(\\.\\d{1,8})?$","description":"Positive decimal string."},"currency":{"type":"string","minLength":3,"maxLength":3,"default":"USD","description":"ISO-4217. Only USD is supported pre-launch."}}},"CreateDepositResponse":{"type":"object","properties":{"data":{"type":"object","properties":{"id":{"type":"string"},"status":{"type":"string","enum":["pending"]},"amount":{"type":"string"},"currency":{"type":"string"},"checkoutUrl":{"type":"string","format":"uri"},"btcpayInvoiceId":{"type":"string"},"expiresAt":{"type":"string","format":"date-time"}}}}},"CreateOrderBody":{"type":"object","required":["productId","variantId","email"],"properties":{"productId":{"type":"string"},"variantId":{"type":"string"},"email":{"type":"string","format":"email"},"firstName":{"type":"string","maxLength":80},"lastName":{"type":"string","maxLength":80},"recipientPhone":{"type":"string","pattern":"^\\+\\d{8,15}$","description":"Required when product.deliveryType=phone_refill. E.164 format."},"recipientCountry":{"type":"string","minLength":2,"maxLength":2,"description":"ISO-3166 alpha-2."},"quantity":{"type":"integer","minimum":1,"maximum":10,"default":1}}},"OrderRow":{"type":"object","properties":{"id":{"type":"string","example":"ord_abc123"},"status":{"type":"string","enum":["awaiting_payment","paid","fulfilling","delivered","expired","failed","refunded"]},"total":{"type":"string"},"currency":{"type":"string"},"createdAt":{"type":"string","format":"date-time"},"deliveredAt":{"type":"string","nullable":true,"format":"date-time"}}},"OrdersPage":{"type":"object","properties":{"data":{"type":"object","properties":{"rows":{"type":"array","items":{"$ref":"#/components/schemas/OrderRow"}},"total":{"type":"integer"},"limit":{"type":"integer"},"offset":{"type":"integer"}}}}},"OrderItem":{"type":"object","properties":{"id":{"type":"string"},"orderId":{"type":"string"},"productId":{"type":"string"},"variantId":{"type":"string"},"vendorId":{"type":"string"},"vendorSku":{"type":"string"},"amount":{"type":"string"},"currency":{"type":"string"},"quantity":{"type":"integer"},"code":{"type":"string","nullable":true,"description":"Redemption code (populated once fulfilled)."},"pin":{"type":"string","nullable":true},"redemptionUrl":{"type":"string","nullable":true,"format":"uri","description":"Voucher redemption link, served as an own-domain /redeem/<alias> URL that 302-redirects to the vendor redemption page."},"codeExpiresAt":{"type":"string","nullable":true,"format":"date-time"},"recipientPhone":{"type":"string","nullable":true},"recipientCountry":{"type":"string","nullable":true},"fulfilledAt":{"type":"string","nullable":true,"format":"date-time"}}},"OrderDetail":{"allOf":[{"$ref":"#/components/schemas/OrderRow"},{"type":"object","properties":{"email":{"type":"string","format":"email"},"firstName":{"type":"string","nullable":true},"lastName":{"type":"string","nullable":true},"paidAt":{"type":"string","nullable":true,"format":"date-time"},"items":{"type":"array","items":{"$ref":"#/components/schemas/OrderItem"}}}}]}}}}