{"info":{"title":"Omra API","version":"1.0.0","description":"REST API for club operations: membership and households, billing, the member portal, the tee sheet, regattas, camps, banquets, point of sale and feedback. One surface over the same member record the Omra app runs on.\n\n## Authentication\n\nSend a key in the `api-key` request header. Keys are created by club staff in the Omra app under Settings → Developer and are scoped to the issuing club. There is no OAuth flow, no bearer form, and no agentic self-registration — an agent cannot obtain a credential on its own. Full walkthrough: https://clubomra.com/auth.md\n\n## Scopes\n\nA key grants only the scopes chosen when it was created, and they are least-privilege: a key holding `members:read` alone reads members and nothing else. A call outside a key's scopes returns 403 `API_KEY_SCOPE_DENIED`.\n\n- `members:read` — Read member and household records\n- `applications:read` — Read membership applications\n- `applications:write` — Submit and update membership applications\n- `billing:read` — Read invoices, payments and line items\n- `orders:read` — Read point-of-sale orders\n- `feedback:read` — Read feedback tied to orders, locations and staff\n- `portal:sessions:create` — Mint a member-portal session\n- `tee_times:read` — Read the tee sheet\n- `scores:write` — Write regatta race scores\n- `wristbands:read` — Read regatta wristband checks\n- `wristbands:write` — Record regatta wristband checks\n- `banquets:read` — Read event orders and their line items\n- `camps:read` — Read camps, sessions and registrations\n\n## Responses\n\nSuccessful responses use the envelope `{ request_id, code, status, data }`. List endpoints return `has_more` alongside the collection and accept `cursor` and `limit` (1–100, default 25); page by the last item's cursor rather than assuming offset paging.\n\n## Idempotency\n\nMutations accept an `idempotency-key` header. The first response is replayed instead of the write being applied twice, scoped per key and per route — so retry a failed mutation with the same value, and use a fresh value for a genuinely new operation.\n\n## Errors\n\n| HTTP | code | Meaning |\n| --- | --- | --- |\n| 401 | `INVALID_API_KEY` | Missing, unknown, revoked or expired key |\n| 403 | `API_KEY_SCOPE_DENIED` | Valid key, but it lacks the scope the route needs |\n| 429 | `RATE_LIMITED` | Too many requests for this key |\n\n## Versioning\n\nVersioned in the path. A client pinned to `/api/v1` keeps working; breaking changes ship as a new path segment, while new fields and routes land in `v1` without notice. Parse defensively and ignore unknown fields."},"servers":[{"url":"/api/v1"}],"components":{"securitySchemes":{"api-key":{"type":"apiKey","in":"header","name":"api-key","description":"Per-club API key, created by club staff under Settings → Developer.\n\nScopes are selected per key and enforced per route:\n\n- `members:read` — Read member and household records\n- `applications:read` — Read membership applications\n- `applications:write` — Submit and update membership applications\n- `billing:read` — Read invoices, payments and line items\n- `orders:read` — Read point-of-sale orders\n- `feedback:read` — Read feedback tied to orders, locations and staff\n- `portal:sessions:create` — Mint a member-portal session\n- `tee_times:read` — Read the tee sheet\n- `scores:write` — Write regatta race scores\n- `wristbands:read` — Read regatta wristband checks\n- `wristbands:write` — Record regatta wristband checks\n- `banquets:read` — Read event orders and their line items\n- `camps:read` — Read camps, sessions and registrations","x-scopes":{"members:read":"Read member and household records","applications:read":"Read membership applications","applications:write":"Submit and update membership applications","billing:read":"Read invoices, payments and line items","orders:read":"Read point-of-sale orders","feedback:read":"Read feedback tied to orders, locations and staff","portal:sessions:create":"Mint a member-portal session","tee_times:read":"Read the tee sheet","scores:write":"Write regatta race scores","wristbands:read":"Read regatta wristband checks","wristbands:write":"Record regatta wristband checks","banquets:read":"Read event orders and their line items","camps:read":"Read camps, sessions and registrations"}}},"parameters":{"IdempotencyKey":{"name":"idempotency-key","in":"header","required":false,"description":"Replays the first response for this key and route instead of applying the write twice. Retry a failed mutation with the same value.","schema":{"type":"string","maxLength":255}}}},"security":[{"api-key":[]}],"openapi":"3.1.1","paths":{"/members":{"get":{"operationId":"members","parameters":[{"name":"club_id","in":"query","required":true,"schema":{"type":"string"},"allowEmptyValue":true,"allowReserved":true},{"name":"primary_only","in":"query","required":false,"schema":{"enum":["true","false"],"type":"string"},"allowEmptyValue":true,"allowReserved":true},{"name":"skip","in":"query","required":false,"schema":{"type":"integer","minimum":0,"maximum":9007199254740991,"default":0},"allowEmptyValue":true,"allowReserved":true}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"anyOf":[{},{"not":{}}]}}}}}}},"/membership-applications":{"post":{"operationId":"submitApplication","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"club_id":{"type":"string"},"plan_id":{"type":"string"},"applicant_data":{"type":"array","minItems":1,"items":{"type":"object","properties":{"primary":{"type":"boolean"},"standard_fields":{"type":"object","properties":{"first_name":{"type":"string","minLength":1},"last_name":{"type":"string","minLength":1},"email":{"type":"string","format":"email"},"mobile":{"type":"string"},"dob":{"type":"object","properties":{"year":{"type":"integer","minimum":1900,"maximum":2200},"month":{"type":"integer","minimum":1,"maximum":12},"date":{"type":"integer","minimum":1,"maximum":31}},"required":["year","month","date"]}},"required":["first_name","last_name"]},"address":{"type":"object","properties":{"street":{"type":"string"},"city":{"type":"string"},"state":{"type":"string"},"zip":{"type":"string"},"country":{"type":"string"}}},"custom_fields":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"data":{"type":"object","properties":{"response":{"anyOf":[{"type":"string"},{"type":"boolean"}]},"selected_options":{"type":"array","items":{"type":"string"}}}}},"required":["id","data"]},"default":[]}},"required":["standard_fields"]}}},"required":["club_id","plan_id","applicant_data"]}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"anyOf":[{},{"not":{}}]}}}}}}},"/membership-applicants":{"get":{"operationId":"applicants","parameters":[{"name":"club_id","in":"query","required":true,"schema":{"type":"string"},"allowEmptyValue":true,"allowReserved":true},{"name":"start_date","in":"query","required":true,"schema":{"type":"integer","minimum":0,"maximum":9007199254740991},"allowEmptyValue":true,"allowReserved":true},{"name":"end_date","in":"query","required":true,"schema":{"type":"integer","minimum":0,"maximum":9007199254740991},"allowEmptyValue":true,"allowReserved":true},{"name":"skip","in":"query","required":false,"schema":{"type":"integer","minimum":0,"maximum":9007199254740991,"default":0},"allowEmptyValue":true,"allowReserved":true}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"anyOf":[{},{"not":{}}]}}}}}}},"/line-items":{"get":{"operationId":"lineItems","parameters":[{"name":"club_id","in":"query","required":true,"schema":{"type":"string"},"allowEmptyValue":true,"allowReserved":true},{"name":"start_date","in":"query","required":true,"schema":{"type":"integer","minimum":0,"maximum":9007199254740991},"allowEmptyValue":true,"allowReserved":true},{"name":"end_date","in":"query","required":true,"schema":{"type":"integer","minimum":0,"maximum":9007199254740991},"allowEmptyValue":true,"allowReserved":true},{"name":"skip","in":"query","required":false,"schema":{"type":"integer","minimum":0,"maximum":9007199254740991,"default":0},"allowEmptyValue":true,"allowReserved":true}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"anyOf":[{},{"not":{}}]}}}}}}},"/aging-report":{"get":{"operationId":"agingReport","parameters":[{"name":"club_id","in":"query","required":true,"schema":{"type":"string"},"allowEmptyValue":true,"allowReserved":true},{"name":"as_of_timestamp","in":"query","required":false,"schema":{"type":"integer","minimum":0,"maximum":9007199254740991},"allowEmptyValue":true,"allowReserved":true}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"anyOf":[{},{"not":{}}]}}}}}}},"/orders":{"get":{"operationId":"orders","parameters":[{"name":"club_id","in":"query","required":true,"schema":{"type":"string"},"allowEmptyValue":true,"allowReserved":true},{"name":"start_date","in":"query","required":true,"schema":{"type":"integer","minimum":0,"maximum":9007199254740991},"allowEmptyValue":true,"allowReserved":true},{"name":"end_date","in":"query","required":true,"schema":{"type":"integer","minimum":0,"maximum":9007199254740991},"allowEmptyValue":true,"allowReserved":true},{"name":"skip","in":"query","required":false,"schema":{"type":"integer","minimum":0,"maximum":9007199254740991,"default":0},"allowEmptyValue":true,"allowReserved":true}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"anyOf":[{},{"not":{}}]}}}}}}},"/member-portal/sessions":{"post":{"operationId":"memberPortalSessions.create","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"membership_number":{"type":"string","minLength":1},"initial_view":{"enum":["home","reservations","billing","account","members","stay"],"type":"string","default":"home"},"display_name":{"type":"string","minLength":1}},"required":["membership_number"]}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"anyOf":[{},{"not":{}}]}}}}}}},"/feedback":{"get":{"operationId":"feedback","parameters":[{"name":"club_id","in":"query","required":true,"schema":{"type":"string"},"allowEmptyValue":true,"allowReserved":true},{"name":"start_date","in":"query","required":true,"schema":{"type":"integer","minimum":0,"maximum":9007199254740991},"allowEmptyValue":true,"allowReserved":true},{"name":"end_date","in":"query","required":true,"schema":{"type":"integer","minimum":0,"maximum":9007199254740991},"allowEmptyValue":true,"allowReserved":true},{"name":"ratings","in":"query","required":false,"schema":{"anyOf":[{"type":"integer","minimum":1,"maximum":5},{"type":"array","items":{"type":"integer","minimum":1,"maximum":5}}]},"allowEmptyValue":true,"allowReserved":true},{"name":"skip","in":"query","required":false,"schema":{"type":"integer","minimum":0,"maximum":9007199254740991,"default":0},"allowEmptyValue":true,"allowReserved":true}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"anyOf":[{},{"not":{}}]}}}}}}},"/tee-times":{"post":{"operationId":"teeTimes","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"club_id":{"type":"string"},"start_date":{"type":"integer","minimum":0,"maximum":9007199254740991},"end_date":{"type":"integer","minimum":0,"maximum":9007199254740991},"skip":{"type":"integer","minimum":0,"maximum":9007199254740991,"default":0}},"required":["club_id","start_date","end_date"]}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"anyOf":[{},{"not":{}}]}}}}}}},"/scores":{"post":{"operationId":"scores","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"finish_time":{"type":"number","minimum":0},"registration_id":{"type":"string"},"race_number":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"exclusiveMinimum":0}},"required":["finish_time","registration_id","race_number"]}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"anyOf":[{},{"not":{}}]}}}}}}},"/wristbands":{"get":{"operationId":"wristbands","parameters":[{"name":"regatta_id","in":"query","required":true,"schema":{"type":"string"},"allowEmptyValue":true,"allowReserved":true},{"name":"status","in":"query","required":false,"schema":{"enum":["ashore","sailing"],"type":"string"},"allowEmptyValue":true,"allowReserved":true},{"name":"skip","in":"query","required":false,"schema":{"type":"integer","minimum":0,"maximum":9007199254740991,"default":0},"allowEmptyValue":true,"allowReserved":true}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"anyOf":[{},{"not":{}}]}}}}}}},"/wristbands/scan_event":{"post":{"operationId":"wristbandScanEvent","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"},"status":{"enum":["ashore","sailing"],"type":"string"},"timestamp":{"type":"integer","minimum":0,"maximum":9007199254740991},"regatta_id":{"type":"string"}},"required":["id","status","timestamp","regatta_id"]}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"anyOf":[{},{"not":{}}]}}}}}}},"/banquets":{"get":{"operationId":"banquets","parameters":[{"name":"club_id","in":"query","required":true,"schema":{"type":"string"},"allowEmptyValue":true,"allowReserved":true},{"name":"start_date","in":"query","required":true,"schema":{"type":"integer","minimum":0,"maximum":9007199254740991},"allowEmptyValue":true,"allowReserved":true},{"name":"end_date","in":"query","required":true,"schema":{"type":"integer","minimum":0,"maximum":9007199254740991},"allowEmptyValue":true,"allowReserved":true},{"name":"skip","in":"query","required":false,"schema":{"type":"integer","minimum":0,"maximum":9007199254740991,"default":0},"allowEmptyValue":true,"allowReserved":true}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"anyOf":[{},{"not":{}}]}}}}}}},"/banquet-line-items":{"get":{"operationId":"banquetLineItems","parameters":[{"name":"club_id","in":"query","required":true,"schema":{"type":"string"},"allowEmptyValue":true,"allowReserved":true},{"name":"event_order_id","in":"query","required":true,"schema":{"type":"string"},"allowEmptyValue":true,"allowReserved":true},{"name":"skip","in":"query","required":false,"schema":{"type":"integer","minimum":0,"maximum":9007199254740991,"default":0},"allowEmptyValue":true,"allowReserved":true}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"anyOf":[{},{"not":{}}]}}}}}}},"/camp-registrations":{"get":{"operationId":"campRegistrations","parameters":[{"name":"club_id","in":"query","required":true,"schema":{"type":"string"},"allowEmptyValue":true,"allowReserved":true},{"name":"start_date","in":"query","required":true,"schema":{"type":"integer","minimum":0,"maximum":9007199254740991},"allowEmptyValue":true,"allowReserved":true},{"name":"end_date","in":"query","required":true,"schema":{"type":"integer","minimum":0,"maximum":9007199254740991},"allowEmptyValue":true,"allowReserved":true},{"name":"camp_id","in":"query","required":false,"schema":{"type":"string"},"allowEmptyValue":true,"allowReserved":true},{"name":"skip","in":"query","required":false,"schema":{"type":"integer","minimum":0,"maximum":9007199254740991,"default":0},"allowEmptyValue":true,"allowReserved":true}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"anyOf":[{},{"not":{}}]}}}}}}}}}