{"openapi":"3.1.0","info":{"title":"KuruMesaju API","version":"1.0.0","description":"Maldives SMS and OTP gateway (+960). User routes use a Bearer API key; device routes use a Bearer device token."},"servers":[{"url":"https:\/\/kurumesaju.rashydh.com\/api\/v1","description":"API v1"}],"tags":[{"name":"User API","description":"SMS and OTP (API key)"},{"name":"Device API","description":"Phone job pull and result (device token)"}],"components":{"securitySchemes":{"ApiKeyAuth":{"type":"http","scheme":"bearer","description":"Operator-issued API key"},"DeviceTokenAuth":{"type":"http","scheme":"bearer","description":"Device token from the Devices screen"}},"schemas":{"SmsJobPayload":{"type":"object","required":["event","id","sms_message_id","to","body"],"properties":{"event":{"type":"string","enum":["sms_pending"]},"id":{"type":"integer"},"sms_message_id":{"type":"integer"},"to":{"type":"string","example":"+9607711097"},"body":{"type":"string"}}},"ValidationError":{"type":"object","properties":{"message":{"type":"string"},"errors":{"type":"object","additionalProperties":{"type":"array","items":{"type":"string"}}}}}}},"paths":{"\/sms":{"post":{"tags":["User API"],"summary":"Queue outbound SMS","security":[{"ApiKeyAuth":[]}],"requestBody":{"required":true,"content":{"application\/json":{"schema":{"type":"object","required":["to","body"],"properties":{"to":{"type":"string","maxLength":64,"example":"+9607711097"},"body":{"type":"string","maxLength":4096},"device_id":{"type":"integer","nullable":true}}}}}},"responses":{"201":{"description":"SMS queued","content":{"application\/json":{"schema":{"type":"object","properties":{"id":{"type":"integer"},"status":{"type":"string","enum":["pending"]},"webhook_pushed":{"type":"boolean"}}}}}},"401":{"description":"Invalid API key"},"422":{"description":"Validation error","content":{"application\/json":{"schema":{"$ref":"#\/components\/schemas\/ValidationError"}}}},"429":{"description":"Rate limit exceeded"}}}},"\/otp\/send":{"post":{"tags":["User API"],"summary":"Send OTP via SMS","security":[{"ApiKeyAuth":[]}],"requestBody":{"required":true,"content":{"application\/json":{"schema":{"type":"object","required":["phone"],"properties":{"phone":{"type":"string","maxLength":64},"device_id":{"type":"integer","nullable":true},"ttl_minutes":{"type":"integer","minimum":1,"maximum":60,"default":10}}}}}},"responses":{"201":{"description":"OTP created and SMS queued","content":{"application\/json":{"schema":{"type":"object","properties":{"otp_id":{"type":"integer"},"sms_message_id":{"type":"integer"},"expires_at":{"type":"string","format":"date-time"},"webhook_pushed":{"type":"boolean"}}}}}},"422":{"description":"Validation error"},"429":{"description":"Rate limit exceeded"}}}},"\/otp\/verify":{"post":{"tags":["User API"],"summary":"Verify OTP code","security":[{"ApiKeyAuth":[]}],"requestBody":{"required":true,"content":{"application\/json":{"schema":{"type":"object","required":["phone","code"],"properties":{"phone":{"type":"string"},"code":{"type":"string","minLength":6,"maxLength":6,"pattern":"^[0-9]{6}$"}}}}}},"responses":{"200":{"description":"Code valid","content":{"application\/json":{"schema":{"type":"object","properties":{"valid":{"type":"boolean","enum":[true]}}}}}},"422":{"description":"Invalid or expired","content":{"application\/json":{"schema":{"type":"object","properties":{"valid":{"type":"boolean","enum":[false]},"message":{"type":"string"}}}}}}}}},"\/device\/jobs\/next":{"get":{"tags":["Device API"],"summary":"Claim next pending SMS job","security":[{"DeviceTokenAuth":[]}],"responses":{"200":{"description":"Job available","content":{"application\/json":{"schema":{"$ref":"#\/components\/schemas\/SmsJobPayload"}}}},"204":{"description":"No pending jobs"},"401":{"description":"Invalid device token"}}}},"\/device\/jobs\/{id}\/result":{"post":{"tags":["Device API"],"summary":"Report SMS send result","security":[{"DeviceTokenAuth":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"integer"}}],"requestBody":{"required":true,"content":{"application\/json":{"schema":{"type":"object","required":["status"],"properties":{"status":{"type":"string","enum":["sent","failed"]},"error":{"type":"string","maxLength":2048}}}}}},"responses":{"200":{"description":"Updated","content":{"application\/json":{"schema":{"type":"object","properties":{"id":{"type":"integer"},"status":{"type":"string","enum":["sent","failed"]}}}}}},"403":{"description":"Device does not own this message"},"422":{"description":"Not dispatched or already finalized"}}}},"\/device\/ping":{"post":{"tags":["Device API"],"summary":"Device heartbeat","security":[{"DeviceTokenAuth":[]}],"responses":{"200":{"description":"OK","content":{"application\/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean","enum":[true]}}}}}}}}}}}