API credentials
Send the key only in the Authorization header over HTTPS. Store it on your server, never in browser code. Keys expire after 90 days; regenerate or revoke them in your dashboard. Up to 5 active keys per membership.
GET https://venomstone.com/api/v1/profile
Authorization: Bearer YOUR_SERVER_SIDE_KEY
Accept: application/json
Operation
| Operation | Permissions |
|---|---|
GET /api/v1/profile | account:read |
GET /api/v1/balance | account:read |
GET /api/v1/status | account:read |
GET /api/v1/platforms | catalog:read |
GET /api/v1/categories | catalog:read |
GET /api/v1/products | catalog:read |
GET /api/v1/products/{product_id} | catalog:read |
GET /api/v1/search | catalog:read |
POST /api/v1/quotes | orders:write |
POST /api/v1/purchases | orders:write |
GET /api/v1/orders | orders:read |
GET /api/v1/orders/{order_id} | orders:read |
GET /api/v1/orders/{order_id}/delivery | delivery:read |
GET /api/v1/orders/{order_id}/files/{file_id} | delivery:read |
GET /api/v1/webhooks | webhooks:manage |
POST /api/v1/webhooks | webhooks:manage |
POST /api/v1/webhooks/{webhook_id}/revoke | webhooks:manage |
Catalog & search
The API lists eligible native and integrated catalog listings from its published snapshot. Website-only provider and service catalogs may not appear here. Snapshot stock is not a reservation.
GET /api/v1/products?q=instagram&in_stock=true&sort=relevance&page=1&per_page=25¤cy=EUR
q: 0–100 characters; results start from the top 100 matches
platform_id / category_id: use ids returned by /platforms and /categories
in_stock: true | false | omitted
min_price / max_price: inclusive USD amounts, 0–1000000
sort: price_asc (default) | price_desc | relevance
page: 1–10000 (default 1); per_page: 1–100 (default 25)
response: items, page, per_page, total, has_next, search_scope,
catalog_updated_at, snapshot_version
In-stock results appear first. With a query, filters apply to a bounded search set; use has_next to page within it. Products can belong to more than one category.
The optional currency on products, search, product detail and balance changes display only. Inspect requested_currency, currency, fallback, stale and rate_date. USD, INR, EUR, GBP, CHF, JPY, CNY, CAD, AUD, SGD, HKD, SEK, NOK, DKK, PLN, CZK, HUF, RON, TRY, BRL, MXN, ZAR, IDR, KRW, NZD, MYR, PHP, THB, ILS
Orders & digital delivery
Discover a product → create a live quote → purchase that quote → poll the order → retrieve ready delivery. Each quote/purchase requires a unique Idempotency-Key; retry with the same key and body after a timeout. Never retry a payment by inventing a new key.
Replace sample IDs with the id returned by the preceding response. Send Bearer authentication on every request, including file downloads. Use separate Idempotency-Key values for quote and purchase (16–128 ASCII letters, digits, underscores or hyphens).
POST /api/v1/quotes
Authorization: Bearer YOUR_SERVER_SIDE_KEY
Idempotency-Key: review-your-order-reference-001
Content-Type: application/json
{"product_id":"PRODUCT_ID_FROM_CATALOG","quantity":1}
POST /api/v1/purchases
Authorization: Bearer YOUR_SERVER_SIDE_KEY
Idempotency-Key: buy-your-order-reference-001
Content-Type: application/json
{"quote_id":"QUOTE_ID_FROM_QUOTE_RESPONSE"}
GET /api/v1/orders/ORDER_ID
GET /api/v1/orders/ORDER_ID/delivery
GET /api/v1/orders/ORDER_ID/files/FILE_ID
Quantity defaults to 1. Listing limits apply; the API caps ordinary products at 10, and eligible bulk products at 200. Only native listings may accept promotion_code. Quotes do not debit the wallet.
Public prices are USD. The ledger remains INR paise. Versioned market-FX quotes lock the reviewed wallet debit until expiry. Display conversions use cached reference rates. Checkout revalidates stock. Promotions use existing coupon rules. Use the returned expires_at, not a fixed countdown. Provider quotes can expire sooner than the FX review. Use wallet_debit.minor_units as the exact INR debit, not the USD display total.
After acceptance, save the order id and poll that order rather than purchasing again. A delivery link requires delivery:read and an owned, authorized record. Some deliveries contain files instead of text; follow files[].download_url with authentication.
awaiting_payment | processing | action_required | ready | refunded | cancelled
quote: id, product_id, quantity, total, wallet_debit, expires_at, fx, pricing_revision
order: id, title, quantity, status, total, created_at, delivery_available, delivery_url
delivery: order_id, text, filename, files[{id, filename, size, download_url}]
file: application/octet-stream; Content-Disposition: attachment
API errors today
Errors contain deterministic codes and retryable status, never provider secrets. Preserve X-Request-ID for support. Respect RateLimit headers and Retry-After. A processing order must not be purchased again. Authenticated requests are limited to 120 per key in a 60-second window. Request bodies are limited to 16 KiB. RateLimit-Reset and Retry-After are seconds, not timestamps.
HTTP 409
{"error":{"code":"request_processing","retryable":true}}
X-Request-ID
RateLimit-Limit, RateLimit-Remaining, RateLimit-Reset
Retry-After (HTTP 429, seconds)
| HTTP | Result | What your integration should do |
|---|---|---|
| 401 | invalid_credential | Check the active key, membership and scope. |
| 403 | permission_denied | Check the active key, membership and scope. |
| 409 | idempotency_conflict | Compare with the original body. Do not resend a changed payment. |
| 409 | request_processing | Back off and retry the same request, key and body. Never create a replacement purchase after an ambiguous response. |
| 409 | insufficient_balance | Add funds in the store; retry the same purchase if the quote is still valid. |
| 409 | quote_expired / pricing_changed | Once you know no order was accepted, make a new quote and review its exact amount. |
| 409 | out_of_stock / price_or_availability_changed | Once you know no order was accepted, make a new quote and review its exact amount. |
| 404 | delivery_not_ready | Check the same order again later; do not repurchase. |
| 413 | request_too_large | Correct the request before retrying. |
| 422 | invalid_request / unsupported_currency | Correct the request before retrying. |
| 429 | rate_limited | Wait for Retry-After, then retry with backoff. |
| 503 | temporarily_unavailable / quote_unavailable | Back off and retry the same request, key and body. Never create a replacement purchase after an ambiguous response. |
Order webhooks
Webhooks carry only event ID, order ID and status. Verify HMAC-SHA256 over timestamp + '.' + exact body, reject stale timestamps and deduplicate event IDs. Deliveries require a separate authenticated request. Non-2xx responses are retried with bounded backoff.
Up to 3 active endpoints and 8 automatic attempts per event. Use public HTTPS on port 443, without query, credentials or fragment. Save the signing secret immediately; it is shown once.
order.action_required, order.processing, order.ready, order.refunded
POST /api/v1/webhooks
{"url":"https://example.com/events","events":["order.ready"]}
POST /api/v1/webhooks/WEBHOOK_ID/revoke
{"expected_version":1}
X-VenomStone-Event-ID: evt_…
X-VenomStone-Timestamp: Unix seconds
X-VenomStone-Signature: v1=<hex HMAC_SHA256(secret, timestamp + '.' + raw_body)>
{"id":"evt_…","type":"order.ready","order_id":"ord_…","status":"ready"}
Use constant-time signature comparison. Save the event durably before returning 2xx. Duplicate or out-of-order events can arrive; the authenticated order endpoint is authoritative.