{"openapi":"3.0.3","info":{"title":"psyke Reseller API","version":"1.0.0","description":"Run bulk phone-number scans programmatically. Submit a number list with a service and a country, we validate and price it against your prepaid wallet, run it, and hand back a downloadable result file. Authenticated with a single X-Reseller-API-Key header.","contact":{"name":"psyke support","url":"https://p-syke.com"}},"servers":[{"url":"https://api.p-syke.com/api/reseller","description":"Production"}],"tags":[{"name":"Catalog","description":"Services, countries and wallet balance (read-only)."},{"name":"Orders","description":"Submit, poll and download bulk scan orders."}],"security":[{"ResellerApiKey":[]}],"paths":{"/api/reseller/services":{"get":{"operationId":"get_services","summary":"List services","description":"Returns the full service catalog (11 services). The code is what you pass as service_code to POST /orders; price_per_number is the USD price charged per number.","tags":["Catalog"],"security":[{"ResellerApiKey":[]}],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object","required":["services"],"properties":{"services":{"type":"array","items":{"$ref":"#/components/schemas/Service"}}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"429":{"$ref":"#/components/responses/Throttled"}}}},"/api/reseller/countries":{"get":{"operationId":"get_countries","summary":"List countries","description":"Returns the supported country catalog (the FMT country list filtered to valid 2-letter ISO codes). Use code (uppercase ISO-2) as the country value on POST /orders.","tags":["Catalog"],"security":[{"ResellerApiKey":[]}],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object","required":["countries"],"properties":{"countries":{"type":"array","items":{"$ref":"#/components/schemas/Country"}}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"429":{"$ref":"#/components/responses/Throttled"}}}},"/api/reseller/balance":{"get":{"operationId":"get_balance","summary":"Get wallet balance","description":"Returns your prepaid wallet balance. 1 token = 1 USD.","tags":["Catalog"],"security":[{"ResellerApiKey":[]}],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Balance"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"429":{"$ref":"#/components/responses/Throttled"}}}},"/api/reseller/orders":{"post":{"operationId":"post_orders","summary":"Submit an order","description":"Submit a phone-number list for scanning. This is the only write endpoint and the only one that spends your wallet. The request is multipart/form-data (file upload).","tags":["Orders"],"security":[{"ResellerApiKey":[]}],"responses":{"201":{"description":"Order created (201) — or 200 with \"replayed\": true on an Idempotency-Key replay.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OrderSubmit"}}}},"400":{"description":"UNKNOWN_SERVICE, UNKNOWN_COUNTRY, VALIDATION_ERROR, FILE_TOO_LARGE, MIN_NUMBERS","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ViewError"},"examples":{"UNKNOWN_SERVICE":{"value":{"code":"UNKNOWN_SERVICE","message":"service_code is not one of the 11 catalog codes."}},"UNKNOWN_COUNTRY":{"value":{"code":"UNKNOWN_COUNTRY","message":"country (after trim + uppercase) is not a supported ISO-2 code."}},"VALIDATION_ERROR":{"value":{"code":"VALIDATION_ERROR","message":"The file field is missing, or the Idempotency-Key header is longer than 64 characters."}},"FILE_TOO_LARGE":{"value":{"code":"FILE_TOO_LARGE","message":"The uploaded file exceeds 20 MB."}},"MIN_NUMBERS":{"value":{"code":"MIN_NUMBERS","message":"Fewer than 2000 valid numbers remain after normalization + dedup. Nothing is charged."}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"402":{"description":"INSUFFICIENT_BALANCE","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ViewError"},"examples":{"INSUFFICIENT_BALANCE":{"value":{"code":"INSUFFICIENT_BALANCE","message":"Your wallet balance is too low to charge the job. No order is created."}}}}}},"409":{"description":"NOT_QUEUEABLE","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ViewError"},"examples":{"NOT_QUEUEABLE":{"value":{"code":"NOT_QUEUEABLE","message":"The order could not be queued (rare internal state)."}}}}}},"429":{"$ref":"#/components/responses/Throttled"},"503":{"description":"ORDER_REF_CONFLICT","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ViewError"},"examples":{"ORDER_REF_CONFLICT":{"value":{"code":"ORDER_REF_CONFLICT","message":"The server could not allocate a unique order id after bounded retries (extremely rare). No charge is made."}}}}}}},"parameters":[{"name":"Idempotency-Key","in":"header","required":false,"schema":{"type":"string","maxLength":64},"description":"Replay guard, ≤ 64 chars, scoped to your account."}],"requestBody":{"required":true,"content":{"multipart/form-data":{"schema":{"type":"object","required":["service_code","country","file"],"properties":{"service_code":{"type":"string","description":"One of the catalog service codes.","example":"tg-active"},"country":{"type":"string","description":"ISO-2 code (uppercased server-side).","example":"US"},"file":{"type":"string","format":"binary","description":"Phone-number list, one per line, UTF-8, ≤ 20 MB."}}}}}}}},"/api/reseller/orders/{order_id}":{"get":{"operationId":"get_order","summary":"Poll order status","description":"Poll order status and metadata. This is the endpoint you loop on to detect completion.","tags":["Orders"],"security":[{"ResellerApiKey":[]}],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OrderDetail"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"description":"NOT_FOUND","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ViewError"},"examples":{"NOT_FOUND":{"value":{"code":"NOT_FOUND","message":"No order matches that id for your account (never existed, malformed id, or belongs to another tenant)."}}}}}},"429":{"$ref":"#/components/responses/Throttled"}},"parameters":[{"name":"order_id","in":"path","required":true,"schema":{"type":"string"},"description":"The order_id returned by POST /orders. Use it verbatim."}]}},"/api/reseller/orders/{order_id}/result":{"get":{"operationId":"get_order_result","summary":"Download result","description":"Download the finished result. The success response is a FILE (CSV or TSV), not JSON. Only call it once GET /orders/{order_id} reports status == \"completed\".","tags":["Orders"],"security":[{"ResellerApiKey":[]}],"responses":{"200":{"description":"Result file download (CSV when a generated export exists, otherwise TSV).","content":{"text/csv":{"schema":{"type":"string","format":"binary"}},"text/tab-separated-values":{"schema":{"type":"string","format":"binary"}}},"headers":{"Content-Disposition":{"schema":{"type":"string"},"description":"attachment; filename=\"order_<order_id>_result.csv|tsv\""}}},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"description":"NOT_FOUND","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ViewError"},"examples":{"NOT_FOUND":{"value":{"code":"NOT_FOUND","message":"No order matches that id for your account (never existed, malformed id, or belongs to another tenant)."}}}}}},"409":{"description":"NOT_READY","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ViewError"},"examples":{"NOT_READY":{"value":{"code":"NOT_READY","message":"The order is not completed yet. The message includes the current status."}}}}}},"429":{"$ref":"#/components/responses/Throttled"},"500":{"description":"RESULT_UNAVAILABLE","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ViewError"},"examples":{"RESULT_UNAVAILABLE":{"value":{"code":"RESULT_UNAVAILABLE","message":"The order is completed but the result payload could not be retrieved (missing export file and no inline text)."}}}}}}},"parameters":[{"name":"order_id","in":"path","required":true,"schema":{"type":"string"},"description":"The order_id returned by POST /orders."}]}}},"components":{"securitySchemes":{"ResellerApiKey":{"type":"apiKey","in":"header","name":"X-Reseller-API-Key","description":"Bearer credential. Keys start with \"rsl_live_\" and are at least 16 characters long."}},"schemas":{"Service":{"type":"object","required":["code","name","platform","price_per_number"],"properties":{"code":{"type":"string","example":"tg-active"},"name":{"type":"string","example":"Telegram active + registered"},"platform":{"type":"string","example":"telegram"},"price_per_number":{"type":"number","format":"double","example":0.0004,"description":"USD per accepted number. May serialize in scientific notation."}}},"Country":{"type":"object","required":["code","name","area_code"],"properties":{"code":{"type":"string","example":"US"},"name":{"type":"string","example":"United States"},"area_code":{"type":"string","example":"1"}}},"Balance":{"type":"object","required":["token_balance","currency"],"properties":{"token_balance":{"type":"string","example":"98.460000","description":"Fixed 6-decimal decimal string."},"currency":{"type":"string","enum":["USD"]}}},"OrderSubmit":{"type":"object","required":["order_id","status","service_code","country","input_count","cost","wallet_balance_after"],"properties":{"order_id":{"type":"string","example":"20260702T034401344985Z","description":"UTC microsecond timestamp, format YYYYMMDDThhmmssffffffZ."},"status":{"type":"string","enum":["draft","queued","running","completed","failed"],"example":"queued"},"service_code":{"type":"string","example":"tg-active"},"country":{"type":"string","example":"US"},"input_count":{"type":"integer","example":2000},"cost":{"type":"string","example":"0.800000","description":"Fixed 6-decimal string = input_count × price_per_number."},"wallet_balance_after":{"type":"string","example":"98.460000"},"replayed":{"type":"boolean","description":"Present and true only on an Idempotency-Key replay (HTTP 200 instead of 201)."}}},"OrderDetail":{"type":"object","required":["order_id","status","service_code","country","input_count","result_count","result_suspect","created_at","completed_at","failure_message"],"properties":{"order_id":{"type":"string","example":"20260702T034401344985Z"},"status":{"type":"string","enum":["draft","queued","running","completed","failed"]},"service_code":{"type":"string","example":"tg-active"},"country":{"type":"string","example":"US"},"input_count":{"type":"integer","example":2000},"result_count":{"type":"integer","example":0},"result_suspect":{"type":"boolean","example":false},"created_at":{"type":"string","format":"date-time"},"completed_at":{"type":"string","format":"date-time","nullable":true},"failure_message":{"type":"string","description":"Empty unless the order failed."}}},"ViewError":{"type":"object","description":"View-returned error: a plain {code, message} object with no is_error field.","required":["code","message"],"properties":{"code":{"type":"string","description":"Machine-readable error code."},"message":{"type":"string","description":"Human-readable explanation."}}},"FrameworkError":{"type":"object","description":"Framework-raised error (auth / throttle), routed through the DRF exception handler which adds is_error.","required":["message","code","is_error"],"properties":{"message":{"type":"string"},"code":{"type":"string"},"is_error":{"type":"boolean","enum":[true]}}}},"responses":{"Unauthorized":{"description":"401 — missing header (INVALID_API_KEY, view shape) or bad/inactive key (authentication_failed, framework shape).","content":{"application/json":{"schema":{"oneOf":[{"$ref":"#/components/schemas/ViewError"},{"$ref":"#/components/schemas/FrameworkError"}]},"examples":{"INVALID_API_KEY":{"value":{"code":"INVALID_API_KEY","message":"The X-Reseller-API-Key header is missing entirely."}},"authentication_failed":{"value":{"message":"The key is malformed (wrong prefix / shorter than 16 chars) or unknown / inactive / revoked.","code":"authentication_failed","is_error":true}}}}}},"Throttled":{"description":"429 — rate limit exceeded (120/min per API key).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FrameworkError"},"examples":{"throttled":{"value":{"message":"You exceeded the rate limit (120/min, per API key).","code":"throttled","is_error":true}}}}}}}},"x-psyke":{"note":"Generated from marketing/src/lib/apiSpec.ts, itself grounded in web/packages/backend/apps/reseller/. Prices are reseller (Telegram-bot) rates.","services":[{"code":"tg-active","price_per_number":0.0004},{"code":"tg-age-gender","price_per_number":0.00077},{"code":"ws-active","price_per_number":0.00014338},{"code":"ws-registered","price_per_number":0.00001544},{"code":"ws-age-gender","price_per_number":0.00035294},{"code":"facebook","price_per_number":0.00005074},{"code":"instagram","price_per_number":0.00005074},{"code":"amazon","price_per_number":0.00024265},{"code":"binance","price_per_number":0.00132353},{"code":"empty-number","price_per_number":0.00035294},{"code":"overseas-carrier","price_per_number":0.000375}],"rate_limit":"120 requests / minute","min_valid_numbers":2000,"max_upload_mb":20}}