{"openapi":"3.1.0","info":{"title":"MediSend Synthetic Integration Sandbox","version":"1.0.0","description":"Separate credentials, tables and namespace. No live medical data, recipients, notifications or billing. All examples use fixed synthetic outcomes. No retirement is scheduled; incompatible changes require a new major namespace and published migration guidance."},"servers":[{"url":"https://api.medisend.bg"}],"security":[{"sandboxBearer":[]}],"paths":{"/api/sandbox/v1/capabilities":{"get":{"summary":"Inspect sandbox environment, scopes and rate limit","responses":{"200":{"description":"Sandbox capabilities","headers":{"X-Request-ID":{"$ref":"#/components/headers/RequestId"},"X-MediSend-Environment":{"$ref":"#/components/headers/Environment"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/Remaining"}},"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","required":["environment","organization_id","synthetic_only","scopes","scenarios","rate_limit_per_minute"],"properties":{"environment":{"const":"sandbox"},"organization_id":{"type":"string"},"synthetic_only":{"const":true},"scopes":{"type":"array","items":{"type":"string"}},"scenarios":{"type":"array","items":{"type":"string"}},"rate_limit_per_minute":{"const":60}}}}}}}},"401":{"$ref":"#/components/responses/Error401"},"403":{"$ref":"#/components/responses/Error403"},"404":{"$ref":"#/components/responses/Error404"},"409":{"$ref":"#/components/responses/Error409"},"422":{"$ref":"#/components/responses/Error422"},"429":{"$ref":"#/components/responses/Error429"},"503":{"$ref":"#/components/responses/Error503"}}}},"/api/sandbox/v1/handovers":{"get":{"summary":"Paginated synthetic handovers in this sandbox organization","x-medisend-scope":"handovers:read","parameters":[{"name":"page","in":"query","schema":{"type":"integer","minimum":1}},{"name":"per_page","in":"query","schema":{"type":"integer","minimum":1,"maximum":100,"default":20}}],"responses":{"200":{"description":"Synthetic handover page","headers":{"X-Request-ID":{"$ref":"#/components/headers/RequestId"},"X-MediSend-Environment":{"$ref":"#/components/headers/Environment"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/Remaining"}},"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/SyntheticHandover"}},"meta":{"type":"object"},"environment":{"const":"sandbox"}}}}}},"401":{"$ref":"#/components/responses/Error401"},"403":{"$ref":"#/components/responses/Error403"},"404":{"$ref":"#/components/responses/Error404"},"409":{"$ref":"#/components/responses/Error409"},"422":{"$ref":"#/components/responses/Error422"},"429":{"$ref":"#/components/responses/Error429"},"503":{"$ref":"#/components/responses/Error503"}}},"post":{"summary":"Create a fixed synthetic handover or simulate provider failure","x-medisend-scope":"handovers:write","parameters":[{"name":"Idempotency-Key","in":"header","schema":{"type":"string","minLength":8,"maxLength":200},"description":"Same credential/key/scenario replays the package with 200. Changed scenario returns 409. Retained with the sandbox record."}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","additionalProperties":false,"required":["scenario"],"properties":{"scenario":{"enum":["delivered","notification_failed","scanner_quarantined","awaiting_recipient"]},"simulate_error":{"enum":["provider_unavailable"]}}}}}},"responses":{"200":{"description":"Idempotent replay","headers":{"X-Request-ID":{"$ref":"#/components/headers/RequestId"},"X-MediSend-Environment":{"$ref":"#/components/headers/Environment"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/Remaining"}},"content":{"application/json":{"schema":{"type":"object","required":["data","environment"],"properties":{"data":{"$ref":"#/components/schemas/SyntheticHandover"},"environment":{"const":"sandbox"},"replayed":{"type":"boolean"}}}}}},"201":{"description":"Created synthetic handover","headers":{"X-Request-ID":{"$ref":"#/components/headers/RequestId"},"X-MediSend-Environment":{"$ref":"#/components/headers/Environment"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/Remaining"}},"content":{"application/json":{"schema":{"type":"object","required":["data","environment"],"properties":{"data":{"$ref":"#/components/schemas/SyntheticHandover"},"environment":{"const":"sandbox"},"replayed":{"type":"boolean"}}}}}},"401":{"$ref":"#/components/responses/Error401"},"403":{"$ref":"#/components/responses/Error403"},"404":{"$ref":"#/components/responses/Error404"},"409":{"$ref":"#/components/responses/Error409"},"422":{"$ref":"#/components/responses/Error422"},"429":{"$ref":"#/components/responses/Error429"},"503":{"$ref":"#/components/responses/Error503"}}}},"/api/sandbox/v1/handovers/{packageId}":{"get":{"summary":"Read a synthetic handover; live and foreign IDs return 404","x-medisend-scope":"handovers:read","parameters":[{"name":"packageId","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Synthetic handover","headers":{"X-Request-ID":{"$ref":"#/components/headers/RequestId"},"X-MediSend-Environment":{"$ref":"#/components/headers/Environment"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/Remaining"}},"content":{"application/json":{"schema":{"type":"object","required":["data","environment"],"properties":{"data":{"$ref":"#/components/schemas/SyntheticHandover"},"environment":{"const":"sandbox"},"replayed":{"type":"boolean"}}}}}},"401":{"$ref":"#/components/responses/Error401"},"403":{"$ref":"#/components/responses/Error403"},"404":{"$ref":"#/components/responses/Error404"},"409":{"$ref":"#/components/responses/Error409"},"422":{"$ref":"#/components/responses/Error422"},"429":{"$ref":"#/components/responses/Error429"},"503":{"$ref":"#/components/responses/Error503"}}}}},"components":{"securitySchemes":{"sandboxBearer":{"type":"http","scheme":"bearer","description":"ms_test_ credential created explicitly by an organization settings administrator. Expires after 30 days; only its hash is stored."}},"schemas":{"SyntheticHandover":{"type":"object","required":["id","organization_id","synthetic","scenario","status","title","file_count","total_bytes","created_at"],"properties":{"id":{"type":"string"},"organization_id":{"type":"string","pattern":"^sbx_"},"synthetic":{"const":true},"scenario":{"enum":["delivered","notification_failed","scanner_quarantined","awaiting_recipient"]},"status":{"enum":["accessed","notification_failed","quarantined","awaiting_recipient"]},"title":{"const":"Synthetic CBCT demonstration"},"file_count":{"const":2},"total_bytes":{"const":1048576},"created_at":{"type":"string"}}},"SandboxError":{"type":"object","required":["error","environment"],"properties":{"error":{"type":"object","required":["code","request_id"],"properties":{"code":{"type":"string"},"request_id":{"type":"string","format":"uuid"}}},"environment":{"const":"sandbox"}}}},"responses":{"Error401":{"description":"Invalid, expired, revoked or live credential","headers":{"X-Request-ID":{"$ref":"#/components/headers/RequestId"},"X-MediSend-Environment":{"$ref":"#/components/headers/Environment"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SandboxError"}}}},"Error403":{"description":"Required sandbox scope absent","headers":{"X-Request-ID":{"$ref":"#/components/headers/RequestId"},"X-MediSend-Environment":{"$ref":"#/components/headers/Environment"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SandboxError"}}}},"Error404":{"description":"Synthetic record absent in this sandbox organization","headers":{"X-Request-ID":{"$ref":"#/components/headers/RequestId"},"X-MediSend-Environment":{"$ref":"#/components/headers/Environment"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SandboxError"}}}},"Error409":{"description":"Idempotency scenario conflict","headers":{"X-Request-ID":{"$ref":"#/components/headers/RequestId"},"X-MediSend-Environment":{"$ref":"#/components/headers/Environment"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SandboxError"}}}},"Error422":{"description":"Invalid request; medical fields and uploads are rejected","headers":{"X-Request-ID":{"$ref":"#/components/headers/RequestId"},"X-MediSend-Environment":{"$ref":"#/components/headers/Environment"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SandboxError"}}}},"Error429":{"description":"Rate limited; obey Retry-After","headers":{"X-Request-ID":{"$ref":"#/components/headers/RequestId"},"X-MediSend-Environment":{"$ref":"#/components/headers/Environment"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/Remaining"},"Retry-After":{"$ref":"#/components/headers/RetryAfter"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SandboxError"}}}},"Error503":{"description":"Simulated provider unavailable","headers":{"X-Request-ID":{"$ref":"#/components/headers/RequestId"},"X-MediSend-Environment":{"$ref":"#/components/headers/Environment"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SandboxError"}}}}},"headers":{"RequestId":{"schema":{"type":"string"},"description":"Correlation reference"},"Environment":{"schema":{"type":"string"},"description":"Always sandbox"},"RateLimit":{"schema":{"type":"string"},"description":"60 requests per minute per credential"},"Remaining":{"schema":{"type":"string"},"description":"Remaining requests in the current window"},"RetryAfter":{"schema":{"type":"integer"},"description":"Seconds until retry"}}}}