{"openapi":"3.0.3","info":{"title":"Pratick Auth API","version":"1.0","description":"Pratick Auth — OTP SMS gateway, authentication, prepaid balance, KYC, and reporting API (public integrator surface).","contact":{"name":"Pratick Support","url":"https://auth.pratick.net/docs"}},"servers":[{"url":"https://auth.pratick.net","description":"Production"}],"tags":[{"name":"OTP Gateway"},{"name":"Other APIs"}],"components":{"securitySchemes":{"ApiKeyAuth":{"type":"apiKey","in":"header","name":"x-pratick-key","description":"Gateway API key (pk_test_* or pk_live_*)"},"BearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT"}},"examples":{"ErrorCatalog":{"value":[{"code":"Unauthorized","http":401,"fix":"Send pk_test_* or pk_live_* in x-pratick-key."},{"code":"Invalid API Key format.","http":401,"fix":"/v1/send requires a gateway key — JWT alone is rejected."},{"code":"Invalid international phone number","http":400,"fix":"Use E.164, e.g. +22890123456."},{"code":"Unsupported country or prefix","http":400,"fix":"Destination not in coverage — see landing page."},{"code":"DELIVERY_FAILED","http":502,"fix":"SMS not delivered; you are not billed. Retry."},{"code":"RATE_LIMITED","http":429,"fix":"Too many sends — wait retryAfter seconds."},{"code":"INSUFFICIENT_BALANCE","http":402,"fix":"Top up wallet before sending with pk_live_*."},{"code":"OTP_EXPIRED","http":200,"fix":"Code TTL elapsed — call /v1/send again."},{"code":"MAX_ATTEMPTS","http":200,"fix":"Too many wrong codes — request a new OTP."},{"code":"Missing or invalid phone or code","http":400,"fix":"Verify body needs phone + 6-digit code."}]}}},"paths":{"/v1/send":{"post":{"operationId":"otp-send","summary":"Send OTP","description":"Send a 6-digit SMS OTP. Live keys dispatch SMS and log the send; test keys simulate success without SMS or charge.","tags":["OTP Gateway"],"responses":{"200":{"description":"Sandbox (pk_test_*)","content":{"application/json":{"schema":{"example":{"status":"delivered","mode":"test","messageId":"ts_…"}}}}},"202":{"description":"Live (queued)","content":{"application/json":{"schema":{"example":{"status":"queued","messageId":"msg_…","cost_cfa":25}}}}},"402":{"description":"Insufficient balance","content":{"application/json":{"schema":{"example":{"success":false,"error":"INSUFFICIENT_BALANCE"}}}}},"429":{"description":"Rate limited","content":{"application/json":{"schema":{"example":{"error":"RATE_LIMITED","retryAfter":42}}}}},"502":{"description":"Delivery failed","content":{"application/json":{"schema":{"example":{"success":false,"error":"DELIVERY_FAILED"}}}}}},"security":[{"ApiKeyAuth":[]}],"parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"phone":{"type":"string","description":"E.164, e.g. +22890123456"},"message":{"type":"string","description":"Your label for logs (OTP text is generated server-side)"}},"required":["phone","message"]}}}}}},"/v1/verify":{"post":{"operationId":"otp-verify","summary":"Verify OTP","description":"Check the code against the latest pending send for that phone. Response success true|false — always HTTP 200 unless validation fails.","tags":["OTP Gateway"],"responses":{"200":{"description":"Locked out","content":{"application/json":{"schema":{"example":{"success":false,"error":"MAX_ATTEMPTS"}}}}},"400":{"description":"Bad input","content":{"application/json":{"schema":{"example":{"success":false,"error":"Missing or invalid phone or code"}}}}}},"security":[{"ApiKeyAuth":[]}],"parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"phone":{"type":"string","description":"Same number as /v1/send"},"code":{"type":"string","description":"6 digits"}},"required":["phone","code"]}}}}}},"/v1/messages/:messageId":{"get":{"operationId":"message-status","summary":"Message delivery status","description":"Poll async send status for a queued OTP (tenant-scoped).","tags":["OTP Gateway"],"responses":{"200":{"description":"Status","content":{"application/json":{"schema":{"example":{"messageId":"msg_…","status":"queued|sent|delivered|failed","createdAt":"2026-07-16T…","deliveredAt":null}}}}}},"security":[{"ApiKeyAuth":[]}],"parameters":[]}},"/api/v1/auth/login":{"post":{"operationId":"auth-login","summary":"Login","description":"Email + password → JWT (for portal/KYC routes, not for /v1/send).","tags":["Other APIs"],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"example":{"success":true,"accessToken":"…","user":{"email":"…","clientId":"…"}}}}}}},"parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"email":{"type":"string","description":""},"password":{"type":"string","description":""}},"required":["email","password"]}}}}}},"/api/v1/auth/signup":{"post":{"operationId":"auth-signup","summary":"Register","description":"Create account; verify email before login. Alias: /api/v1/auth/register.","tags":["Other APIs"],"responses":{"201":{"description":"Created","content":{"application/json":{"schema":{"example":{"success":true,"clientId":"PRAT-042","emailVerificationRequired":true}}}}},"400":{"description":"Terms not accepted","content":{"application/json":{"schema":{"example":{"error":"TERMS_NOT_ACCEPTED"}}}}}},"parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"companyName":{"type":"string","description":""},"firstName":{"type":"string","description":""},"lastName":{"type":"string","description":""},"email":{"type":"string","description":""},"password":{"type":"string","description":"8+ chars, upper, lower, special"},"confirmPassword":{"type":"string","description":""},"phone":{"type":"string","description":"E.164"},"acceptTerms":{"type":"boolean","description":"Must be true — user accepts Terms of Service and Privacy Policy"}},"required":["companyName","firstName","lastName","email","password","confirmPassword","phone","acceptTerms"]}}}}}},"/api/v1/auth/accept-terms":{"post":{"operationId":"auth-accept-terms","summary":"Accept Terms & Privacy","description":"Record acceptance of the current Terms of Service and Privacy Policy (required after version updates).","tags":["Other APIs"],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"example":{"success":true,"termsVersionAccepted":"2026-07-20","privacyVersionAccepted":"2026-07-20"}}}}},"400":{"description":"Not accepted","content":{"application/json":{"schema":{"example":{"error":"TERMS_NOT_ACCEPTED"}}}}}},"security":[{"ApiKeyAuth":[]}],"parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"acceptTerms":{"type":"boolean","description":"Must be true"}},"required":["acceptTerms"]}}}}}},"/api/v1/auth/me":{"get":{"operationId":"auth-me","summary":"Current user","description":"Profile, KYC status, balance — requires JWT.","tags":["Other APIs"],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"example":{"success":true,"user":{"email":"…","kycStatus":"APPROVED"}}}}}}},"security":[{"ApiKeyAuth":[]}]}},"/wallet/balance":{"get":{"operationId":"wallet-balance","summary":"Prepaid balance","description":"Read remaining CFA credits. Fund account in the client portal — not via API. JWT path: GET /api/v1/wallet/balance.","tags":["Other APIs"],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"example":{"walletBalanceCfa":12500,"currency":"XOF"}}}}}},"security":[{"BearerAuth":[]}]}},"/api/v1/otp-sends":{"get":{"operationId":"otp-sends-list","summary":"OTP history","description":"Recent sends for your tenant. Query: limit (1–200). Export: …/export/excel or …/export/pdf.","tags":["Other APIs"],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"example":[{"sentAt":"…","country":"Togo","phoneMasked":"+228***3456","costCfa":25,"otpVerified":true}]}}}}},"security":[{"ApiKeyAuth":[]}],"parameters":[{"name":"limit","in":"query","required":false,"schema":{"type":"integer"},"description":"Default 100"}]}},"/api/v1/support/ticket":{"post":{"operationId":"support-ticket","summary":"Support ticket","description":"subject (1–200 chars) + message (1–8000 chars).","tags":["Other APIs"],"responses":{"201":{"description":"OK","content":{"application/json":{"schema":{"example":{"success":true,"ticketId":"tk_…"}}}}}},"security":[{"ApiKeyAuth":[]}],"parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"subject":{"type":"string","description":""},"message":{"type":"string","description":""}},"required":["subject","message"]}}}}}}}}