{"openapi":"3.0.3","info":{"title":"Ethio-Viral API","version":"1.0.0","description":"Buy gift cards, airtime, eSIM and subscriptions programmatically. One key, one call to order. Human-readable guide: https://api.ethio-viral.com/docs/v1","contact":{"name":"Support","url":"https://t.me/muay011"}},"servers":[{"url":"https://api.ethio-viral.com/v1","description":"Production"}],"security":[{"BearerAuth":[]},{"ApiKeyHeader":[]}],"paths":{"/catalog":{"get":{"summary":"List every product, paged","description":"The complete supplier catalog (over 10,000 products), not the curated storefront shelves. Page through it once and cache it rather than calling per lookup. `complete: false` means a supplier sync is still running — the products returned are valid, there are simply more coming.","parameters":[{"name":"page","in":"query","description":"1-based page number.","schema":{"type":"integer","minimum":1,"default":1}},{"name":"per_page","in":"query","description":"Products per page.","schema":{"type":"integer","minimum":1,"maximum":1000,"default":200}},{"name":"since","in":"query","description":"Epoch ms — pass the `synced_at` you last saw and receive only products that changed since. Turns a daily 20-page re-sync into a handful of rows.","schema":{"type":"integer"}}],"responses":{"200":{"description":"A page of products.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CatalogPage"}}}},"401":{"description":"Missing, invalid or revoked API key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limited — retry after the minute window.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/catalog/categories":{"get":{"summary":"List categories with product counts","responses":{"200":{"description":"Categories.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Categories"}}}},"401":{"description":"Missing, invalid or revoked API key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limited — retry after the minute window.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/catalog/category/{cat}":{"get":{"summary":"List products in one category","parameters":[{"name":"cat","in":"path","required":true,"description":"Category key, e.g. `giftcards`.","schema":{"type":"string"}}],"responses":{"200":{"description":"Products.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProductList"}}}},"401":{"description":"Missing, invalid or revoked API key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limited — retry after the minute window.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/catalog/search":{"get":{"summary":"Full-text search the catalog","parameters":[{"name":"q","in":"query","required":true,"description":"Search text, e.g. `netflix`.","schema":{"type":"string"}}],"responses":{"200":{"description":"Matching products.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProductList"}}}},"401":{"description":"Missing, invalid or revoked API key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limited — retry after the minute window.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/catalog/sections":{"get":{"summary":"List the storefront merchandising shelves","responses":{"200":{"description":"Sections.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Sections"}}}},"401":{"description":"Missing, invalid or revoked API key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limited — retry after the minute window.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/catalog/section/{key}":{"get":{"summary":"List products on one shelf","parameters":[{"name":"key","in":"path","required":true,"description":"Section key from /catalog/sections.","schema":{"type":"string"}},{"name":"country","in":"query","description":"ISO country filter, or `ALL`.","schema":{"type":"string"}}],"responses":{"200":{"description":"Shelf contents.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Section"}}}},"401":{"description":"Missing, invalid or revoked API key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limited — retry after the minute window.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/catalog/product/{id}":{"get":{"summary":"Get one product and its orderable denominations","description":"Call this before ordering. `denoms[].retailMinor` gives the exact values POST /orders accepts, and `recipientType` tells you what the buyer must supply.","parameters":[{"name":"id","in":"path","required":true,"description":"Product id, e.g. `uq-ABC123`.","schema":{"type":"string"}}],"responses":{"200":{"description":"The product.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProductEnvelope"}}}},"401":{"description":"Missing, invalid or revoked API key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"No such product.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limited — retry after the minute window.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/balance":{"get":{"summary":"Get your wallet balance","description":"Orders are paid from this balance. Top up before a large batch.","responses":{"200":{"description":"Your balance.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Balance"}}}},"401":{"description":"Missing, invalid or revoked API key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limited — retry after the minute window.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/orders":{"get":{"summary":"List your orders, newest first","responses":{"200":{"description":"Your orders.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OrderList"}}}},"401":{"description":"Missing, invalid or revoked API key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limited — retry after the minute window.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"post":{"summary":"Create an order (debits your wallet)","description":"Idempotency-Key is required: without it a retried request after a timeout would buy twice. Reusing the same key with the same body is safe and returns the original order — so retry freely, but never reuse a key for a different purchase.","parameters":[{"name":"Idempotency-Key","in":"header","required":true,"description":"Your own unique string per purchase. Safe to retry with.","schema":{"type":"string","maxLength":200}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/NewOrder"}}}},"responses":{"200":{"description":"The created order.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OrderEnvelope"}}}},"400":{"description":"Unknown productId, or a denomination the product does not offer.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing, invalid or revoked API key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"402":{"description":"Not enough wallet balance.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Out of stock or temporarily unavailable.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limited — retry after the minute window.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/orders/{id}":{"get":{"summary":"Get one order and its delivery","description":"Poll until `status` is `fulfilled`, then read `deliveryPayload` — the code, link or voucher. There is no separate delivery endpoint. To avoid polling, register a webhook at `POST /v1/webhooks` and we will call you on every terminal state instead. Lifecycle: paid → processing → fulfilled, or refunded straight back to your wallet on failure.","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"The order.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OrderEnvelope"}}}},"401":{"description":"Missing, invalid or revoked API key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Not your order.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"No such order.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limited — retry after the minute window.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/premium/products":{"get":{"summary":"List premium subscription products","description":"Netflix, Spotify, ChatGPT, Canva and similar seats, with your tier pricing and stock.","responses":{"200":{"description":"Premium products.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProductList"}}}},"401":{"description":"Missing, invalid or revoked API key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limited — retry after the minute window.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/premium/products/{id}":{"get":{"summary":"Get one premium product","parameters":[{"name":"id","in":"path","required":true,"description":"Premium product id, e.g. `prm_…`.","schema":{"type":"string"}}],"responses":{"200":{"description":"The product.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProductEnvelope"}}}},"401":{"description":"Missing, invalid or revoked API key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"No such product.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limited — retry after the minute window.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/premium/balance":{"get":{"summary":"Premium wallet balance (USD)","description":"Premium orders settle in USD; the store wallet is ETB. Check this one before a premium order.","responses":{"200":{"description":"Balance.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Balance"}}}},"401":{"description":"Missing, invalid or revoked API key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limited — retry after the minute window.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/premium/orders":{"get":{"summary":"List your premium orders","responses":{"200":{"description":"Orders.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OrderList"}}}},"401":{"description":"Missing, invalid or revoked API key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limited — retry after the minute window.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"post":{"summary":"Order a premium subscription (debits your USD wallet)","description":"Same Idempotency-Key rule as the store: required, and safe to retry with.","parameters":[{"name":"Idempotency-Key","in":"header","required":true,"description":"Your own unique string per purchase.","schema":{"type":"string","maxLength":200}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/NewPremiumOrder"}}}},"responses":{"200":{"description":"The order.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OrderEnvelope"}}}},"401":{"description":"Missing, invalid or revoked API key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"402":{"description":"Not enough premium balance.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Unavailable or out of stock.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limited — retry after the minute window.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/premium/orders/{id}":{"get":{"summary":"Get one premium order","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"The order.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OrderEnvelope"}}}},"401":{"description":"Missing, invalid or revoked API key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"No such order.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limited — retry after the minute window.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/premium/orders/{id}/delivery":{"get":{"summary":"Collect the delivery once fulfilled","description":"Premium delivery is a separate call, unlike the store where deliveryPayload is inline. Poll the order until fulfilled, then read this.","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Delivery code or link.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Delivery"}}}},"401":{"description":"Missing, invalid or revoked API key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not fulfilled yet, or no such order.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limited — retry after the minute window.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/smm/services":{"get":{"summary":"List SMM services","description":"Followers, likes, views and similar, with your rate per 1,000 and the min/max quantity each accepts.","responses":{"200":{"description":"Services.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SmmServices"}}}},"401":{"description":"Missing, invalid or revoked API key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limited — retry after the minute window.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/smm/orders":{"post":{"summary":"Create an SMM order (debits your wallet)","description":"SMM takes a service, a link and a quantity — not a productId and denomination. Quantity must be within that service's min and max.","parameters":[{"name":"Idempotency-Key","in":"header","required":true,"description":"Your own unique string per order.","schema":{"type":"string","maxLength":200}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/NewSmmOrder"}}}},"responses":{"200":{"description":"The order, plus the full order object.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SmmOrderCreated"}}}},"400":{"description":"Bad request. The `error` names which: quantity outside the service min/max, `subscription_requires_posts`, `subscription_requires_quantity_per_post`, or quantity-per-post out of range.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing, invalid or revoked API key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"402":{"description":"Not enough balance.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"`service_not_found` — no active service with that id.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"`idempotency_key_payload_mismatch` — that key was already used with a different body.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limited — retry after the minute window.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"`invalid_service_price` — the stored rate produced an impossible charge. Should not happen; report it.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"502":{"description":"`provider_rejected_refunded` — the supplier refused and YOUR WALLET HAS ALREADY BEEN REFUNDED. Treat as a clean failure: do not retry the same Idempotency-Key, and do not tell your customer it succeeded.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/webhook":{"get":{"summary":"Get your webhook endpoint","responses":{"200":{"description":"Your endpoint.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookEnvelope"}}}},"401":{"description":"Missing, invalid or revoked API key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"No webhook configured.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limited — retry after the minute window.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"put":{"summary":"Set your webhook endpoint","description":"Returns the signing secret ONCE — we sign with it, so it cannot be shown again. Saving a new URL always issues a new secret. https only, and private/loopback addresses are refused.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/NewWebhook"}}}},"responses":{"200":{"description":"Saved, with the secret.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookSaved"}}}},"400":{"description":"Invalid url, or no valid events.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing, invalid or revoked API key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limited — retry after the minute window.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"delete":{"summary":"Remove your webhook endpoint","responses":{"200":{"description":"Removed.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Removed"}}}},"401":{"description":"Missing, invalid or revoked API key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limited — retry after the minute window.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/webhook/test":{"post":{"summary":"Send a signed test delivery","description":"Sends a real signed order.fulfilled to your URL so you can prove verification works before a live order depends on it.","responses":{"200":{"description":"Delivery result.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookTest"}}}},"401":{"description":"Missing, invalid or revoked API key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"No webhook configured.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limited — retry after the minute window.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/smm/orders/{id}":{"get":{"summary":"Get one SMM order with real progress","description":"Returns start_count and remains from the provider, so you can show true progress rather than assuming 100%.","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"The order, the provider view, and what the reconcile did.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SmmOrderStatus"}}}},"401":{"description":"Missing, invalid or revoked API key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"No such order.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limited — retry after the minute window.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}}},"components":{"securitySchemes":{"BearerAuth":{"type":"http","scheme":"bearer","description":"Authorization: Bearer evk_YOUR_KEY"},"ApiKeyHeader":{"type":"apiKey","in":"header","name":"X-API-Key","description":"X-API-Key: evk_YOUR_KEY"}},"schemas":{"Error":{"type":"object","properties":{"error":{"type":"string","description":"A short machine-readable reason."}}},"Denom":{"type":"object","properties":{"label":{"type":"string","description":"Display label, e.g. \"$10.53\"."},"face":{"type":"number","description":"Value printed on the product. Display only — never send it as the price."},"faceCurrency":{"type":"string","description":"Currency of `face`, e.g. USD."},"retailMinor":{"type":"integer","description":"What you pay, in ETB santim (1/100 birr). 199000 = ETB 1,990.00. Send this exact number as `denomination`."}}},"Product":{"type":"object","properties":{"id":{"type":"string","example":"uq-ZLCYydbpQQPeIo"},"name":{"type":"string"},"brand":{"type":"string"},"country":{"type":"string","description":"ISO country, or XI for global."},"productType":{"type":"string","enum":["giftcard","airtime","esim"]},"recipientType":{"type":"string","enum":["email","phone_number","none"],"description":"What `recipient` must contain when ordering."},"instant":{"type":"boolean","description":"true delivers in seconds; otherwise expect minutes."},"inStock":{"type":"boolean","nullable":true,"description":"true in stock, false out of stock, null when we do not track stock for that product. Never assume null means available."},"fields":{"type":"array","items":{"type":"string"},"description":"Extra values this product requires, e.g. [\"playerid\"]. Send them in `fields` when ordering."},"image":{"type":"string","description":"Absolute, directly loadable logo URL."},"denoms":{"type":"array","items":{"$ref":"#/components/schemas/Denom"}}}},"ProductList":{"type":"object","properties":{"products":{"type":"array","items":{"$ref":"#/components/schemas/Product"}}}},"ProductEnvelope":{"type":"object","properties":{"product":{"$ref":"#/components/schemas/Product"}}},"CatalogPage":{"type":"object","properties":{"products":{"type":"array","items":{"$ref":"#/components/schemas/Product"}},"page":{"type":"integer"},"per_page":{"type":"integer"},"total_items":{"type":"integer"},"total_pages":{"type":"integer"},"complete":{"type":"boolean","description":"false while a supplier sync is still running."},"synced_at":{"type":"integer","nullable":true,"description":"Epoch ms of the last full sync. Store it and pass it back as ?since= next time."},"since":{"type":"integer","nullable":true,"description":"The ?since= you sent, echoed back."},"delta":{"type":"boolean","description":"true when this response contains only changed products."}}},"Categories":{"type":"object","properties":{"categories":{"type":"array","items":{"type":"object","properties":{"category":{"type":"string"},"count":{"type":"integer"}}}}}},"Section":{"type":"object","properties":{"key":{"type":"string"},"items":{"type":"array","items":{"$ref":"#/components/schemas/Product"}}}},"Sections":{"type":"object","properties":{"sections":{"type":"array","items":{"$ref":"#/components/schemas/Section"}}}},"Balance":{"type":"object","properties":{"balanceMinor":{"type":"integer","description":"Your wallet in ETB santim."}}},"NewOrder":{"type":"object","required":["productId","denomination"],"properties":{"productId":{"type":"string","description":"From the catalog."},"denomination":{"type":"integer","description":"A `retailMinor` from that product’s `denoms`."},"recipient":{"type":"string","description":"Email, phone or account id — per the product’s `recipientType`."},"quantity":{"type":"integer","default":1},"fields":{"type":"object","additionalProperties":{"type":"string"},"description":"Extra supplier fields when a product asks for them (e.g. playerid)."}}},"Order":{"type":"object","properties":{"id":{"type":"string"},"status":{"type":"string","enum":["paid","processing","fulfilled","refunded","failed"]},"product":{"type":"string"},"deliveryPayload":{"type":"string","nullable":true,"description":"The code, link or voucher once fulfilled."},"recipient":{"type":"string","nullable":true}}},"OrderEnvelope":{"type":"object","properties":{"order":{"$ref":"#/components/schemas/Order"}}},"OrderList":{"type":"object","properties":{"orders":{"type":"array","items":{"$ref":"#/components/schemas/Order"}}}},"Delivery":{"type":"object","properties":{"code":{"type":"string","nullable":true,"description":"The redemption code, when the product has one."},"link":{"type":"string","nullable":true,"description":"The invite or activation link, when the product uses one."}}},"NewPremiumOrder":{"type":"object","required":["productId"],"properties":{"productId":{"type":"string","example":"prm_netflix_1m"},"quantity":{"type":"integer","default":1},"recipient":{"type":"string","description":"Email the seat or invite is delivered to."}}},"SmmService":{"type":"object","properties":{"service":{"type":"string","description":"Service id — send this as `serviceId` when ordering."},"name":{"type":"string"},"category":{"type":"string","example":"Instagram"},"rate":{"type":"string","description":"Your price per 1,000 units."},"min":{"type":"string","description":"Smallest quantity this service accepts."},"max":{"type":"string","description":"Largest quantity this service accepts."},"refill":{"type":"boolean","description":"Whether a refill can be requested for this service."},"cancel":{"type":"boolean","description":"Whether an order can be cancelled."}}},"SmmServices":{"type":"object","properties":{"services":{"type":"array","items":{"$ref":"#/components/schemas/SmmService"}}}},"NewSmmOrder":{"type":"object","required":["serviceId","link","quantity"],"properties":{"serviceId":{"type":"string","description":"From /smm/services."},"link":{"type":"string","description":"The profile, post or channel to deliver to."},"quantity":{"type":"integer","description":"Must be between that service's min and max."},"params":{"type":"object","additionalProperties":{"type":"string"},"description":"Extra values some services require (e.g. comments, runs, interval)."}}},"SmmOrder":{"type":"object","properties":{"id":{"type":"string"},"status":{"type":"string","enum":["pending","processing","completed","partial","canceled","refunded"]},"start_count":{"type":"string","description":"Follower/view count when the order started."},"remains":{"type":"string","description":"How many units are still outstanding. 0 means delivered."}}},"SmmOrderEnvelope":{"type":"object","properties":{"order":{"$ref":"#/components/schemas/SmmOrder"}}},"SmmOrderCreated":{"type":"object","properties":{"order_id":{"type":"string","description":"The provider-facing order id."},"oracle_order_id":{"type":"string","description":"Our order id — use this with GET /smm/orders/{id}."},"status":{"type":"string"},"order":{"$ref":"#/components/schemas/Order","description":"The full order, so you need no second call to show the customer what they bought."}}},"SmmOrderStatus":{"type":"object","properties":{"order":{"$ref":"#/components/schemas/SmmOrder"},"provider":{"type":"object","description":"The supplier own view of the order."},"action":{"type":"string","description":"What this status check did — whether it reconciled anything on this call."}}},"NewWebhook":{"type":"object","required":["url"],"properties":{"url":{"type":"string","description":"https endpoint we POST to. Private and loopback addresses are refused."},"events":{"type":"array","items":{"type":"string","enum":["order.fulfilled","order.refunded","order.failed"]},"description":"Omit for all events."}}},"Webhook":{"type":"object","properties":{"url":{"type":"string"},"events":{"type":"array","items":{"type":"string"}},"status":{"type":"string","enum":["active","disabled"]},"secretPrefix":{"type":"string","description":"First characters only — enough to identify which secret you hold."},"lastStatus":{"type":"integer","nullable":true,"description":"HTTP status of the last delivery attempt."},"lastSuccessAt":{"type":"string","nullable":true},"consecutiveFailures":{"type":"integer","description":"Resets to 0 on any success."}}},"WebhookEnvelope":{"type":"object","properties":{"webhook":{"$ref":"#/components/schemas/Webhook"}}},"WebhookSaved":{"type":"object","properties":{"webhook":{"$ref":"#/components/schemas/Webhook"},"secret":{"type":"string","description":"Shown once. Store it now."},"note":{"type":"string"}}},"WebhookTest":{"type":"object","properties":{"delivered":{"type":"boolean"},"attempts":{"type":"integer"},"status":{"type":"integer","nullable":true}}},"Removed":{"type":"object","properties":{"removed":{"type":"boolean"}}}}}}