{"openapi":"3.1.0","info":{"title":"The API AAR Needs Pathos to Build","version":"0.1.0 · Proposal, draft for Pathos review","description":"**This is a proposal, not an existing API.** It describes the endpoints, request parameters, and response shapes that the Athlete Agent Registration portal (AAR) needs **Pathos** to build and expose, so the two systems can integrate. It is our opinionated starting point for that build, open to discussion and collaboration.\n\nNote that this lays out **two distinct systems**, Background Check and Education, each under its own path prefix (`/background-check/*` and `/education/*`). The two share almost all of their shape, so Pathos may well prefer to **consolidate them into a single API**. We only separated them in case Pathos would rather build and run them as separate systems; either approach is fine by us.\n\nSee the [process overview](/) for the end-to-end model and the reasoning behind each decision."},"servers":[{"url":"https://aar-api-demo.avodah.review","description":"Live demo"},{"url":"http://localhost:3430","description":"Local dev"}],"components":{"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","description":"Bearer API key issued to AAR. Documented here; NOT enforced in this demo."}},"schemas":{"BackgroundCheckLookupResponse":{"type":"object","properties":{"applicant":{"$ref":"#/components/schemas/Applicant"},"backgroundChecks":{"type":"array","items":{"$ref":"#/components/schemas/BackgroundCheckRecord"}}},"required":["applicant","backgroundChecks"]},"Applicant":{"type":"object","properties":{"applicantId":{"type":"string","description":"The vendor system's stable internal ID (GUID) for this applicant.","example":"756e06a4-d7ad-4ac2-9b7c-44b65e7c3820"},"email":{"type":"string","format":"email","example":"jane.smith@example.com"},"name":{"type":"string","example":"Jane Smith"},"dateOfBirth":{"type":"string","pattern":"^\\d{4}-\\d{2}-\\d{2}$","example":"1990-04-12"},"createdAt":{"type":"string","format":"date-time","example":"2026-05-01T14:30:00.000Z"},"updatedAt":{"type":"string","format":"date-time","example":"2026-05-20T09:15:00.000Z"}},"required":["applicantId","email","name","dateOfBirth","createdAt","updatedAt"]},"BackgroundCheckRecord":{"type":"object","properties":{"backgroundCheckId":{"type":"string","example":"2dd5c1b3-c23d-43dc-948e-34a9371a8ac2"},"status":{"type":"string","enum":["not_started","in_progress","approved","rejected"],"description":"A single status phrase. No numeric score — status plus description covers it.","example":"approved"},"description":{"type":["string","null"],"example":"Identity verified; no disqualifying records found."},"completedAt":{"type":["string","null"],"format":"date-time","example":"2026-05-18T16:00:00.000Z"},"createdAt":{"type":"string","format":"date-time","example":"2026-05-02T10:00:00.000Z"},"updatedAt":{"type":"string","format":"date-time","example":"2026-05-18T16:00:00.000Z"}},"required":["backgroundCheckId","status","description","completedAt","createdAt","updatedAt"]},"Error":{"type":"object","properties":{"error":{"type":"string","example":"not_found"},"message":{"type":"string","example":"No applicant matches the provided email and date of birth."}},"required":["error","message"]},"ValidationError":{"type":"object","properties":{"error":{"type":"string","example":"validation_error"},"message":{"type":"string","example":"Request failed validation."},"issues":{"type":"array","items":{},"example":[{"path":["dateOfBirth"],"message":"dateOfBirth must be an ISO date (YYYY-MM-DD)"}]}},"required":["error","message","issues"]},"WebhookSubscriptionResponse":{"type":"object","properties":{"subscriptionId":{"type":"string","example":"5f9a2c7d-3e41-4b8a-92c1-6d0e4f7a8b29"},"url":{"type":"string","format":"uri"},"environment":{"type":"string","enum":["production","sandbox"]},"signingSecretLabel":{"type":"string","description":"Identifies which signing secret will sign events for this target.","example":"whsec_sandbox_****"},"createdAt":{"type":"string","format":"date-time"}},"required":["subscriptionId","url","environment","signingSecretLabel","createdAt"]},"WebhookSubscriptionRequest":{"type":"object","properties":{"url":{"type":"string","format":"uri","example":"https://aar.ag.la.gov/webhooks/background-check"},"environment":{"type":"string","enum":["production","sandbox"],"example":"sandbox"}},"required":["url","environment"]},"WebhookTestResponse":{"type":"object","properties":{"headers":{"$ref":"#/components/schemas/WebhookHeaders"},"body":{"$ref":"#/components/schemas/WebhookEventBody"}},"required":["headers","body"]},"WebhookHeaders":{"type":"object","properties":{"Content-Type":{"type":"string","example":"application/json"},"X-Signature":{"type":"string","description":"HMAC-SHA256 over `{X-Timestamp}.{the raw JSON body}`.","example":"sha256=3b8c0f7d2e5a9c1b4f6e8a0d2c4b6e8f1a3c5d7e9b1f3a5c7e9d1b3f5a7c9e1b3"},"X-Timestamp":{"type":"string","description":"Unix epoch seconds; covered by the signature to bound replay.","example":"1780000000"}},"required":["Content-Type","X-Signature","X-Timestamp"]},"WebhookEventBody":{"type":"object","properties":{"eventId":{"type":"string","example":"0b4e7a9c-1f2d-4a83-9c6e-5d7b8a1f2c34"},"eventType":{"type":"string","example":"background_check.approved"},"email":{"type":"string","format":"email","example":"jane.smith@example.com"},"occurredAt":{"type":"string","format":"date-time","example":"2026-05-31T17:45:00.000Z"}},"required":["eventId","eventType","email","occurredAt"]},"WebhookTestRequest":{"type":"object","properties":{"email":{"type":"string","format":"email","example":"jane.smith@example.com"},"eventType":{"type":"string","example":"background_check.approved"}},"required":["email","eventType"]},"KeyRotateResponse":{"type":"object","properties":{"apiKey":{"type":"string","description":"The freshly minted, prefixed, long, random key.","example":"pathos_bg_3f9a...c1"},"expiresAt":{"type":"string","format":"date-time","description":"When this key expires; rotate on an agreed cadence, e.g. 90 or 120 days."},"rotatedAt":{"type":"string","format":"date-time"},"note":{"type":"string","example":"Illustrative: this demo does not enforce or persist keys. In production AAR can hold more than one active key, so a new key is added and confirmed before the old one is retired."}},"required":["apiKey","expiresAt","rotatedAt","note"]},"KeyRotateRequest":{"type":"object","properties":{"clientSecret":{"type":"string","minLength":1,"description":"The separately held client secret (held by AAR in its secrets vault).","example":"cs_demo_secret_value"}},"required":["clientSecret"]},"EducationLookupResponse":{"type":"object","properties":{"applicant":{"$ref":"#/components/schemas/Applicant"},"education":{"type":"array","items":{"$ref":"#/components/schemas/Enrollment"}}},"required":["applicant","education"]},"Enrollment":{"type":"object","properties":{"enrollmentId":{"type":"string","example":"7e1692d1-52cc-44e4-990d-97681ec255a7"},"period":{"type":["string","null"],"description":"e.g. \"2026\" or a date range, if applicable.","example":"2026"},"status":{"type":"string","enum":["not_started","in_progress","complete","incomplete"],"description":"Overall status for the cycle — did they complete all prescribed material?","example":"complete"},"description":{"type":["string","null"],"example":"All prescribed courses completed for the 2026 cycle."},"completedAt":{"type":["string","null"],"format":"date-time","example":"2026-05-12T18:00:00.000Z"},"courses":{"type":"array","items":{"$ref":"#/components/schemas/Course"}},"updatedAt":{"type":"string","format":"date-time","example":"2026-05-12T18:00:00.000Z"}},"required":["enrollmentId","period","status","description","completedAt","courses","updatedAt"]},"Course":{"type":"object","properties":{"courseId":{"type":"string","example":"1862347e-4b16-4944-9d42-45bd76c7ae0d"},"name":{"type":"string","example":"NIL Fundamentals & Athlete Rights"},"status":{"type":"string","enum":["complete","incomplete"],"example":"complete"},"completedAt":{"type":["string","null"],"format":"date-time","example":"2026-05-10T13:20:00.000Z"}},"required":["courseId","name","status","completedAt"]}},"parameters":{}},"paths":{"/background-check/applicants/{applicantId}":{"get":{"tags":["Background Check"],"summary":"Look up an applicant by internal ID","description":"Returns the applicant identity plus this system's records.","parameters":[{"schema":{"type":"string","example":"756e06a4-d7ad-4ac2-9b7c-44b65e7c3820"},"required":true,"name":"applicantId","in":"path"}],"responses":{"200":{"description":"Applicant found.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BackgroundCheckLookupResponse"}}}},"404":{"description":"No applicant with that ID.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/background-check/applicants":{"get":{"tags":["Background Check"],"summary":"Look up an applicant by email + date of birth","description":"Two-factor identity match. BOTH `email` and `dateOfBirth` are required; omitting either fails validation. Email match is case-insensitive.","parameters":[{"schema":{"type":"string","format":"email","example":"jane.smith@example.com"},"required":true,"name":"email","in":"query"},{"schema":{"type":"string","pattern":"^\\d{4}-\\d{2}-\\d{2}$","example":"1990-04-12"},"required":true,"name":"dateOfBirth","in":"query"}],"responses":{"200":{"description":"Applicant found.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BackgroundCheckLookupResponse"}}}},"400":{"description":"Missing or malformed query parameters.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ValidationError"}}}},"404":{"description":"No applicant matches that email + date of birth.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/background-check/webhooks/subscriptions":{"post":{"tags":["Background Check"],"summary":"Register an AAR webhook callback target","description":"AAR registers a callback URL. Supports separate production and sandbox targets with separate signing secrets. Echo-back only in this demo (no persistence).","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookSubscriptionRequest"}}}},"responses":{"201":{"description":"Subscription registered.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookSubscriptionResponse"}}}},"400":{"description":"Invalid subscription request.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ValidationError"}}}}}}},"/background-check/webhooks/test":{"post":{"tags":["Background Check"],"summary":"Preview the exact signed webhook AAR would receive","description":"Shows the exact HTTP request AAR would receive at your webhook URL: the `headers` (including the signature) and the JSON `body`, shown separately. No live delivery. The `X-Signature` is HMAC-SHA256 over `{X-Timestamp}.{the raw JSON body}`; recompute it to verify the scheme.","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookTestRequest"}}}},"responses":{"200":{"description":"The signed request preview.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookTestResponse"}}}},"400":{"description":"Invalid request.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ValidationError"}}}}}}},"/background-check/keys/rotate":{"post":{"tags":["Background Check"],"summary":"Rotate the API key (current key + client secret)","description":"Mints a new key. Gated by the current API key (Authorization header) PLUS a separate client secret. Documented but NOT enforced in this demo — illustrative output.","security":[{"bearerAuth":[]}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/KeyRotateRequest"}}}},"responses":{"200":{"description":"A freshly minted key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/KeyRotateResponse"}}}},"400":{"description":"Missing client secret.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ValidationError"}}}}}}},"/education/applicants/{applicantId}":{"get":{"tags":["Education"],"summary":"Look up an applicant by internal ID","description":"Returns the applicant identity plus this system's records.","parameters":[{"schema":{"type":"string","example":"322dc964-4455-4f16-b9c5-b4669bed5164"},"required":true,"name":"applicantId","in":"path"}],"responses":{"200":{"description":"Applicant found.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EducationLookupResponse"}}}},"404":{"description":"No applicant with that ID.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/education/applicants":{"get":{"tags":["Education"],"summary":"Look up an applicant by email + date of birth","description":"Two-factor identity match. BOTH `email` and `dateOfBirth` are required; omitting either fails validation. Email match is case-insensitive.","parameters":[{"schema":{"type":"string","format":"email","example":"jane.smith@example.com"},"required":true,"name":"email","in":"query"},{"schema":{"type":"string","pattern":"^\\d{4}-\\d{2}-\\d{2}$","example":"1990-04-12"},"required":true,"name":"dateOfBirth","in":"query"}],"responses":{"200":{"description":"Applicant found.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EducationLookupResponse"}}}},"400":{"description":"Missing or malformed query parameters.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ValidationError"}}}},"404":{"description":"No applicant matches that email + date of birth.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/education/webhooks/subscriptions":{"post":{"tags":["Education"],"summary":"Register an AAR webhook callback target","description":"AAR registers a callback URL. Supports separate production and sandbox targets with separate signing secrets. Echo-back only in this demo (no persistence).","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookSubscriptionRequest"}}}},"responses":{"201":{"description":"Subscription registered.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookSubscriptionResponse"}}}},"400":{"description":"Invalid subscription request.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ValidationError"}}}}}}},"/education/webhooks/test":{"post":{"tags":["Education"],"summary":"Preview the exact signed webhook AAR would receive","description":"Shows the exact HTTP request AAR would receive at your webhook URL: the `headers` (including the signature) and the JSON `body`, shown separately. No live delivery. The `X-Signature` is HMAC-SHA256 over `{X-Timestamp}.{the raw JSON body}`; recompute it to verify the scheme.","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookTestRequest"}}}},"responses":{"200":{"description":"The signed request preview.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookTestResponse"}}}},"400":{"description":"Invalid request.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ValidationError"}}}}}}},"/education/keys/rotate":{"post":{"tags":["Education"],"summary":"Rotate the API key (current key + client secret)","description":"Mints a new key. Gated by the current API key (Authorization header) PLUS a separate client secret. Documented but NOT enforced in this demo — illustrative output.","security":[{"bearerAuth":[]}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/KeyRotateRequest"}}}},"responses":{"200":{"description":"A freshly minted key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/KeyRotateResponse"}}}},"400":{"description":"Missing client secret.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ValidationError"}}}}}}}},"webhooks":{}}