{"openapi":"3.1.0","info":{"title":"ANISIVO Partner API","version":"1.0.0","description":"Clinic-authorized partner API with isolated sandbox and production credentials."},"servers":[{"url":"/api/partner/v1","description":"Current environment; credentials are environment-bound"}],"security":[{"bearerAuth":[]}],"paths":{"/oauth/token":{"post":{"security":[],"summary":"Exchange client credentials","operationId":"exchangeToken","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TokenRequest"}}}},"responses":{"200":{"description":"Short-lived access token","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TokenResponse"}}}},"401":{"$ref":"#/components/responses/Unauthorized"}}}},"/context":{"get":{"summary":"Get authorized clinic context","operationId":"getClinicContext","parameters":[{"$ref":"#/components/parameters/ClinicId"}],"responses":{"200":{"description":"Authorized installation context"},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/appointments":{"get":{"summary":"List appointments","operationId":"listPartnerAppointments","parameters":[{"$ref":"#/components/parameters/ClinicId"},{"$ref":"#/components/parameters/BranchId"},{"name":"from","in":"query","schema":{"type":"string","format":"date-time"}},{"name":"to","in":"query","schema":{"type":"string","format":"date-time"}}],"responses":{"200":{"description":"Appointment page"},"403":{"$ref":"#/components/responses/Forbidden"}}},"post":{"summary":"Create an appointment","operationId":"createPartnerAppointment","parameters":[{"$ref":"#/components/parameters/ClinicId"},{"$ref":"#/components/parameters/IdempotencyKey"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["branchId","animalId","scheduledAt"],"properties":{"branchId":{"type":"string","format":"uuid"},"animalId":{"type":"string","format":"uuid"},"scheduledAt":{"type":"string","format":"date-time"},"notes":{"type":"string","maxLength":2000}}}}}},"responses":{"201":{"description":"Created"},"409":{"description":"Idempotency conflict"},"422":{"description":"Validation error"}}}},"/sandbox":{"get":{"summary":"Get sandbox state","operationId":"getSandboxState","responses":{"200":{"description":"Current isolated sandbox"}}},"post":{"summary":"Reset sandbox fixtures","operationId":"resetSandbox","responses":{"200":{"description":"Fresh deterministic fixture"}}}},"/webhooks":{"get":{"summary":"List webhook endpoints","operationId":"listWebhookEndpoints","parameters":[{"$ref":"#/components/parameters/ClinicId"}],"responses":{"200":{"description":"Endpoints"}}},"post":{"summary":"Create webhook endpoint","operationId":"createWebhookEndpoint","parameters":[{"$ref":"#/components/parameters/ClinicId"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["url","events"],"properties":{"url":{"type":"string","format":"uri"},"events":{"type":"array","items":{"type":"string","enum":["appointment.created","appointment.updated"]}}}}}}},"responses":{"201":{"description":"Created; signing secret is shown once"},"400":{"description":"Invalid or unsafe destination"}}}},"/webhooks/{endpointId}":{"delete":{"summary":"Revoke webhook endpoint","operationId":"revokeWebhookEndpoint","parameters":[{"$ref":"#/components/parameters/ClinicId"},{"$ref":"#/components/parameters/EndpointId"}],"responses":{"200":{"description":"Revoked"},"404":{"description":"Not found"}}},"post":{"summary":"Rotate webhook signing secret","operationId":"rotateWebhookSecret","parameters":[{"$ref":"#/components/parameters/ClinicId"},{"$ref":"#/components/parameters/EndpointId"}],"responses":{"200":{"description":"New secret shown once"},"409":{"description":"Concurrent rotation conflict"}}}},"/webhooks/{endpointId}/deliveries":{"get":{"summary":"List recent deliveries","operationId":"listWebhookDeliveries","parameters":[{"$ref":"#/components/parameters/ClinicId"},{"$ref":"#/components/parameters/EndpointId"}],"responses":{"200":{"description":"Delivery log"}}},"post":{"summary":"Replay a failed delivery","operationId":"replayWebhookDelivery","parameters":[{"$ref":"#/components/parameters/ClinicId"},{"$ref":"#/components/parameters/EndpointId"}],"responses":{"202":{"description":"Replay queued"},"409":{"description":"Delivery is not replayable"}}}}},"components":{"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"ANISIVO credential or access token"}},"parameters":{"ClinicId":{"name":"X-ANISIVO-Clinic-ID","in":"header","required":true,"schema":{"type":"string","format":"uuid"}},"BranchId":{"name":"X-ANISIVO-Branch-ID","in":"header","required":false,"schema":{"type":"string","format":"uuid"}},"IdempotencyKey":{"name":"Idempotency-Key","in":"header","required":true,"schema":{"type":"string","minLength":8,"maxLength":128}},"EndpointId":{"name":"endpointId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}},"schemas":{"TokenRequest":{"type":"object","required":["client_id","client_secret","grant_type","environment"],"properties":{"client_id":{"type":"string","format":"uuid"},"client_secret":{"type":"string"},"grant_type":{"const":"client_credentials"},"environment":{"type":"string","enum":["sandbox","production"]},"scope":{"type":"string"}}},"TokenResponse":{"type":"object","required":["access_token","token_type","expires_in","scope"],"properties":{"access_token":{"type":"string"},"token_type":{"const":"Bearer"},"expires_in":{"type":"integer","const":900},"scope":{"type":"string"}}},"Error":{"type":"object","required":["ok","error","correlationId"],"properties":{"ok":{"const":false},"error":{"type":"string"},"correlationId":{"type":"string","format":"uuid"}}}},"responses":{"Unauthorized":{"description":"Missing or invalid credential","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Forbidden":{"description":"Installation or scope denied","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"RateLimited":{"description":"Rate limit exceeded","headers":{"RateLimit-Remaining":{"schema":{"type":"integer"}},"RateLimit-Reset":{"schema":{"type":"string","format":"date-time"}}}}}}}