# Hapana Developers — Full Reference Site: https://docs.hapana-developers.com --- # Hapana Member API ## v2 # GET /public-api/v2/member/profile **Hapana Member API v2** **Base URL**: `https://api.hapana-app.com/public-api/v2/member` ## Summary Get member profile **Tags**: Member API **Authentication**: ```json [ { "bearerAuth": [] } ] ``` ## Parameters | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | X-Site-ID | header | string | yes | Site/location id. | ## Responses ### 200 — Member profile. ```json { "description": "Member profile." } ``` ### 401 — ```json { "$ref": "#/components/responses/Error" } ``` ### 404 — ```json { "$ref": "#/components/responses/Error" } ``` ## Schemas in scope Refer to the full OpenAPI spec for resolved `$ref` definitions: - https://docs.hapana-developers.com/openapi/member-v2.json --- Source spec: `docs/openapi/hapana-api.openapi.json` · operationId `get_member_profile` · version `v2` ## Security schemes ```json { "apiKey": { "type": "http", "scheme": "bearer", "description": "Brand API key: `Authorization: Bearer hk_live_...` (or `hk_test_...` in test mode). Create and rotate keys in Control Center → API Integrations." }, "bearerAuth": { "type": "http", "scheme": "bearer", "bearerFormat": "JWT", "description": "Firebase ID token for `/public-api/v2/member/*` — obtained by signing the member in with your corporate’s Firebase tenant. Passed as `Authorization: Bearer ` alongside the `X-Site-ID` header." } } ``` # PATCH /public-api/v2/member/profile **Hapana Member API v2** **Base URL**: `https://api.hapana-app.com/public-api/v2/member` ## Summary Update member profile **Tags**: Member API **Authentication**: ```json [ { "bearerAuth": [] } ] ``` ## Parameters | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | X-Site-ID | header | string | yes | Site/location id. | ## Request Body ```json { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "firstName": { "type": "string" }, "lastName": { "type": "string" }, "email": { "type": "string", "format": "email" }, "phone": { "type": [ "string", "null" ] }, "dateOfBirth": { "type": [ "string", "null" ] } } } } } } ``` ## Responses ### 200 — Updated member profile. ```json { "description": "Updated member profile." } ``` ### 400 — ```json { "$ref": "#/components/responses/Error" } ``` ### 401 — ```json { "$ref": "#/components/responses/Error" } ``` ## Schemas in scope Refer to the full OpenAPI spec for resolved `$ref` definitions: - https://docs.hapana-developers.com/openapi/member-v2.json --- Source spec: `docs/openapi/hapana-api.openapi.json` · operationId `update_member_profile` · version `v2` ## Security schemes ```json { "apiKey": { "type": "http", "scheme": "bearer", "description": "Brand API key: `Authorization: Bearer hk_live_...` (or `hk_test_...` in test mode). Create and rotate keys in Control Center → API Integrations." }, "bearerAuth": { "type": "http", "scheme": "bearer", "bearerFormat": "JWT", "description": "Firebase ID token for `/public-api/v2/member/*` — obtained by signing the member in with your corporate’s Firebase tenant. Passed as `Authorization: Bearer ` alongside the `X-Site-ID` header." } } ``` # GET /public-api/v2/member/schedule **Hapana Member API v2** **Base URL**: `https://api.hapana-app.com/public-api/v2/member` ## Summary List upcoming sessions for the member **Tags**: Member API **Authentication**: ```json [ { "bearerAuth": [] } ] ``` ## Parameters | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | X-Site-ID | header | string | yes | Site/location id. | | startDate | query | string | no | Start date (YYYY-MM-DD). | | endDate | query | string | no | End date (YYYY-MM-DD). | | limit | query | integer | no | Max results (1–100). | ## Responses ### 200 — Upcoming sessions. ```json { "description": "Upcoming sessions." } ``` ### 401 — ```json { "$ref": "#/components/responses/Error" } ``` ## Schemas in scope Refer to the full OpenAPI spec for resolved `$ref` definitions: - https://docs.hapana-developers.com/openapi/member-v2.json --- Source spec: `docs/openapi/hapana-api.openapi.json` · operationId `list_member_schedule` · version `v2` ## Security schemes ```json { "apiKey": { "type": "http", "scheme": "bearer", "description": "Brand API key: `Authorization: Bearer hk_live_...` (or `hk_test_...` in test mode). Create and rotate keys in Control Center → API Integrations." }, "bearerAuth": { "type": "http", "scheme": "bearer", "bearerFormat": "JWT", "description": "Firebase ID token for `/public-api/v2/member/*` — obtained by signing the member in with your corporate’s Firebase tenant. Passed as `Authorization: Bearer ` alongside the `X-Site-ID` header." } } ``` # GET /public-api/v2/member/schedule/{id} **Hapana Member API v2** **Base URL**: `https://api.hapana-app.com/public-api/v2/member` ## Summary Get a session from the member schedule **Tags**: Member API **Authentication**: ```json [ { "bearerAuth": [] } ] ``` ## Parameters | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | X-Site-ID | header | string | yes | Site/location id. | | id | path | string | yes | Session id. | ## Responses ### 200 — Session details. ```json { "description": "Session details." } ``` ### 401 — ```json { "$ref": "#/components/responses/Error" } ``` ### 404 — ```json { "$ref": "#/components/responses/Error" } ``` ## Schemas in scope Refer to the full OpenAPI spec for resolved `$ref` definitions: - https://docs.hapana-developers.com/openapi/member-v2.json --- Source spec: `docs/openapi/hapana-api.openapi.json` · operationId `get_member_session` · version `v2` ## Security schemes ```json { "apiKey": { "type": "http", "scheme": "bearer", "description": "Brand API key: `Authorization: Bearer hk_live_...` (or `hk_test_...` in test mode). Create and rotate keys in Control Center → API Integrations." }, "bearerAuth": { "type": "http", "scheme": "bearer", "bearerFormat": "JWT", "description": "Firebase ID token for `/public-api/v2/member/*` — obtained by signing the member in with your corporate’s Firebase tenant. Passed as `Authorization: Bearer ` alongside the `X-Site-ID` header." } } ``` # GET /public-api/v2/member/bookings **Hapana Member API v2** **Base URL**: `https://api.hapana-app.com/public-api/v2/member` ## Summary List the member's bookings **Tags**: Member API **Authentication**: ```json [ { "bearerAuth": [] } ] ``` ## Parameters | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | X-Site-ID | header | string | yes | Site/location id. | | status | query | string | no | Filter by status (comma-separated). | | startDate | query | string | no | Filter start date. | | endDate | query | string | no | Filter end date. | | limit | query | integer | no | Max results. | ## Responses ### 200 — Member bookings. ```json { "description": "Member bookings." } ``` ### 401 — ```json { "$ref": "#/components/responses/Error" } ``` ## Schemas in scope Refer to the full OpenAPI spec for resolved `$ref` definitions: - https://docs.hapana-developers.com/openapi/member-v2.json --- Source spec: `docs/openapi/hapana-api.openapi.json` · operationId `list_member_bookings` · version `v2` ## Security schemes ```json { "apiKey": { "type": "http", "scheme": "bearer", "description": "Brand API key: `Authorization: Bearer hk_live_...` (or `hk_test_...` in test mode). Create and rotate keys in Control Center → API Integrations." }, "bearerAuth": { "type": "http", "scheme": "bearer", "bearerFormat": "JWT", "description": "Firebase ID token for `/public-api/v2/member/*` — obtained by signing the member in with your corporate’s Firebase tenant. Passed as `Authorization: Bearer ` alongside the `X-Site-ID` header." } } ``` # POST /public-api/v2/member/bookings **Hapana Member API v2** **Base URL**: `https://api.hapana-app.com/public-api/v2/member` ## Summary Create a booking **Tags**: Member API **Authentication**: ```json [ { "bearerAuth": [] } ] ``` ## Parameters | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | X-Site-ID | header | string | yes | Site/location id. | ## Request Body ```json { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "sessionId": { "type": "string" }, "spotId": { "type": "integer" }, "notes": { "type": "string" } }, "required": [ "sessionId" ] } } } } ``` ## Responses ### 201 — Booking created. ```json { "description": "Booking created.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Booking" } } } } ``` ### 400 — ```json { "$ref": "#/components/responses/Error" } ``` ### 401 — ```json { "$ref": "#/components/responses/Error" } ``` ### 422 — ```json { "$ref": "#/components/responses/Error" } ``` ## Schemas in scope Refer to the full OpenAPI spec for resolved `$ref` definitions: - https://docs.hapana-developers.com/openapi/member-v2.json --- Source spec: `docs/openapi/hapana-api.openapi.json` · operationId `create_member_booking` · version `v2` ## Security schemes ```json { "apiKey": { "type": "http", "scheme": "bearer", "description": "Brand API key: `Authorization: Bearer hk_live_...` (or `hk_test_...` in test mode). Create and rotate keys in Control Center → API Integrations." }, "bearerAuth": { "type": "http", "scheme": "bearer", "bearerFormat": "JWT", "description": "Firebase ID token for `/public-api/v2/member/*` — obtained by signing the member in with your corporate’s Firebase tenant. Passed as `Authorization: Bearer ` alongside the `X-Site-ID` header." } } ``` # DELETE /public-api/v2/member/bookings/{id} **Hapana Member API v2** **Base URL**: `https://api.hapana-app.com/public-api/v2/member` ## Summary Cancel a booking **Tags**: Member API **Authentication**: ```json [ { "bearerAuth": [] } ] ``` ## Parameters | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | X-Site-ID | header | string | yes | Site/location id. | | id | path | string | yes | Booking id. | ## Responses ### 204 — Booking cancelled. ```json { "description": "Booking cancelled." } ``` ### 401 — ```json { "$ref": "#/components/responses/Error" } ``` ### 403 — ```json { "$ref": "#/components/responses/Error" } ``` ### 404 — ```json { "$ref": "#/components/responses/Error" } ``` ### 422 — ```json { "$ref": "#/components/responses/Error" } ``` ## Schemas in scope Refer to the full OpenAPI spec for resolved `$ref` definitions: - https://docs.hapana-developers.com/openapi/member-v2.json --- Source spec: `docs/openapi/hapana-api.openapi.json` · operationId `cancel_member_booking` · version `v2` ## Security schemes ```json { "apiKey": { "type": "http", "scheme": "bearer", "description": "Brand API key: `Authorization: Bearer hk_live_...` (or `hk_test_...` in test mode). Create and rotate keys in Control Center → API Integrations." }, "bearerAuth": { "type": "http", "scheme": "bearer", "bearerFormat": "JWT", "description": "Firebase ID token for `/public-api/v2/member/*` — obtained by signing the member in with your corporate’s Firebase tenant. Passed as `Authorization: Bearer ` alongside the `X-Site-ID` header." } } ``` # POST /public-api/v2/member/check-in **Hapana Member API v2** **Base URL**: `https://api.hapana-app.com/public-api/v2/member` ## Summary Self check-in at a site or into a session **Tags**: Member API **Authentication**: ```json [ { "bearerAuth": [] } ] ``` ## Parameters | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | X-Site-ID | header | string | yes | Site/location id. | ## Request Body ```json { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "sessionId": { "type": "string" }, "locationId": { "type": "string" } } } } } } ``` ## Responses ### 201 — Check-in recorded. ```json { "description": "Check-in recorded.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Checkin" } } } } ``` ### 400 — ```json { "$ref": "#/components/responses/Error" } ``` ### 401 — ```json { "$ref": "#/components/responses/Error" } ``` ### 422 — ```json { "$ref": "#/components/responses/Error" } ``` ## Schemas in scope Refer to the full OpenAPI spec for resolved `$ref` definitions: - https://docs.hapana-developers.com/openapi/member-v2.json --- Source spec: `docs/openapi/hapana-api.openapi.json` · operationId `member_checkin` · version `v2` ## Security schemes ```json { "apiKey": { "type": "http", "scheme": "bearer", "description": "Brand API key: `Authorization: Bearer hk_live_...` (or `hk_test_...` in test mode). Create and rotate keys in Control Center → API Integrations." }, "bearerAuth": { "type": "http", "scheme": "bearer", "bearerFormat": "JWT", "description": "Firebase ID token for `/public-api/v2/member/*` — obtained by signing the member in with your corporate’s Firebase tenant. Passed as `Authorization: Bearer ` alongside the `X-Site-ID` header." } } ``` # GET /public-api/v2/member/packages **Hapana Member API v2** **Base URL**: `https://api.hapana-app.com/public-api/v2/member` ## Summary List the member's purchased packages **Tags**: Member API **Authentication**: ```json [ { "bearerAuth": [] } ] ``` ## Parameters | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | X-Site-ID | header | string | yes | Site/location id. | ## Responses ### 200 — Member packages. ```json { "description": "Member packages." } ``` ### 401 — ```json { "$ref": "#/components/responses/Error" } ``` ## Schemas in scope Refer to the full OpenAPI spec for resolved `$ref` definitions: - https://docs.hapana-developers.com/openapi/member-v2.json --- Source spec: `docs/openapi/hapana-api.openapi.json` · operationId `list_member_packages` · version `v2` ## Security schemes ```json { "apiKey": { "type": "http", "scheme": "bearer", "description": "Brand API key: `Authorization: Bearer hk_live_...` (or `hk_test_...` in test mode). Create and rotate keys in Control Center → API Integrations." }, "bearerAuth": { "type": "http", "scheme": "bearer", "bearerFormat": "JWT", "description": "Firebase ID token for `/public-api/v2/member/*` — obtained by signing the member in with your corporate’s Firebase tenant. Passed as `Authorization: Bearer ` alongside the `X-Site-ID` header." } } ``` # GET /public-api/v2/member/packages/available **Hapana Member API v2** **Base URL**: `https://api.hapana-app.com/public-api/v2/member` ## Summary List packages available for purchase at the site **Tags**: Member API **Authentication**: ```json [ { "bearerAuth": [] } ] ``` ## Parameters | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | X-Site-ID | header | string | yes | Site/location id. | ## Responses ### 200 — Available packages. Each has `promotional: true` when it is an intro/promotional offer. ```json { "description": "Available packages. Each has `promotional: true` when it is an intro/promotional offer." } ``` ### 401 — ```json { "$ref": "#/components/responses/Error" } ``` ## Schemas in scope Refer to the full OpenAPI spec for resolved `$ref` definitions: - https://docs.hapana-developers.com/openapi/member-v2.json --- Source spec: `docs/openapi/hapana-api.openapi.json` · operationId `list_available_packages` · version `v2` ## Security schemes ```json { "apiKey": { "type": "http", "scheme": "bearer", "description": "Brand API key: `Authorization: Bearer hk_live_...` (or `hk_test_...` in test mode). Create and rotate keys in Control Center → API Integrations." }, "bearerAuth": { "type": "http", "scheme": "bearer", "bearerFormat": "JWT", "description": "Firebase ID token for `/public-api/v2/member/*` — obtained by signing the member in with your corporate’s Firebase tenant. Passed as `Authorization: Bearer ` alongside the `X-Site-ID` header." } } ``` # GET /public-api/v2/member/packages/{id} **Hapana Member API v2** **Base URL**: `https://api.hapana-app.com/public-api/v2/member` ## Summary Get a purchased package **Tags**: Member API **Authentication**: ```json [ { "bearerAuth": [] } ] ``` ## Parameters | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | X-Site-ID | header | string | yes | Site/location id. | | id | path | string | yes | Package id. | ## Responses ### 200 — Package detail. ```json { "description": "Package detail." } ``` ### 401 — ```json { "$ref": "#/components/responses/Error" } ``` ### 404 — ```json { "$ref": "#/components/responses/Error" } ``` ## Schemas in scope Refer to the full OpenAPI spec for resolved `$ref` definitions: - https://docs.hapana-developers.com/openapi/member-v2.json --- Source spec: `docs/openapi/hapana-api.openapi.json` · operationId `get_member_package` · version `v2` ## Security schemes ```json { "apiKey": { "type": "http", "scheme": "bearer", "description": "Brand API key: `Authorization: Bearer hk_live_...` (or `hk_test_...` in test mode). Create and rotate keys in Control Center → API Integrations." }, "bearerAuth": { "type": "http", "scheme": "bearer", "bearerFormat": "JWT", "description": "Firebase ID token for `/public-api/v2/member/*` — obtained by signing the member in with your corporate’s Firebase tenant. Passed as `Authorization: Bearer ` alongside the `X-Site-ID` header." } } ``` # GET /public-api/v2/member/agreements **Hapana Member API v2** **Base URL**: `https://api.hapana-app.com/public-api/v2/member` ## Summary Get signed agreements for the member **Tags**: Member API **Authentication**: ```json [ { "bearerAuth": [] } ] ``` ## Parameters | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | X-Site-ID | header | string | yes | Site/location id. | ## Responses ### 200 — Signed policies, package agreements, and multi-signature agreements. ```json { "description": "Signed policies, package agreements, and multi-signature agreements." } ``` ### 401 — ```json { "$ref": "#/components/responses/Error" } ``` ## Schemas in scope Refer to the full OpenAPI spec for resolved `$ref` definitions: - https://docs.hapana-developers.com/openapi/member-v2.json --- Source spec: `docs/openapi/hapana-api.openapi.json` · operationId `get_member_agreements` · version `v2` ## Security schemes ```json { "apiKey": { "type": "http", "scheme": "bearer", "description": "Brand API key: `Authorization: Bearer hk_live_...` (or `hk_test_...` in test mode). Create and rotate keys in Control Center → API Integrations." }, "bearerAuth": { "type": "http", "scheme": "bearer", "bearerFormat": "JWT", "description": "Firebase ID token for `/public-api/v2/member/*` — obtained by signing the member in with your corporate’s Firebase tenant. Passed as `Authorization: Bearer ` alongside the `X-Site-ID` header." } } ``` # POST /public-api/v2/member/agreements **Hapana Member API v2** **Base URL**: `https://api.hapana-app.com/public-api/v2/member` ## Summary Record a signed agreement (JSON text signature, or multipart package-waiver file) **Tags**: Member API **Authentication**: ```json [ { "bearerAuth": [] } ] ``` ## Parameters | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | X-Site-ID | header | string | yes | Site/location id. | ## Request Body ```json { "required": true, "content": { "application/json": { "schema": { "type": "object", "description": "Standalone agreement (application/json). For a package waiver, send multipart/form-data with a `signature` file part and `packageId` instead.", "properties": { "agreementId": { "type": "string" }, "signature": { "type": "string", "description": "Text signature." } }, "required": [ "agreementId", "signature" ] } } } } ``` ## Responses ### 201 — Signature recorded (returns the signature id). ```json { "description": "Signature recorded (returns the signature id)." } ``` ### 400 — ```json { "$ref": "#/components/responses/Error" } ``` ### 401 — ```json { "$ref": "#/components/responses/Error" } ``` ## Schemas in scope Refer to the full OpenAPI spec for resolved `$ref` definitions: - https://docs.hapana-developers.com/openapi/member-v2.json --- Source spec: `docs/openapi/hapana-api.openapi.json` · operationId `sign_member_agreement` · version `v2` ## Security schemes ```json { "apiKey": { "type": "http", "scheme": "bearer", "description": "Brand API key: `Authorization: Bearer hk_live_...` (or `hk_test_...` in test mode). Create and rotate keys in Control Center → API Integrations." }, "bearerAuth": { "type": "http", "scheme": "bearer", "bearerFormat": "JWT", "description": "Firebase ID token for `/public-api/v2/member/*` — obtained by signing the member in with your corporate’s Firebase tenant. Passed as `Authorization: Bearer ` alongside the `X-Site-ID` header." } } ``` # GET /public-api/v2/member/agreements/pending **Hapana Member API v2** **Base URL**: `https://api.hapana-app.com/public-api/v2/member` ## Summary List agreements awaiting the member’s signature **Tags**: Member API **Authentication**: ```json [ { "bearerAuth": [] } ] ``` ## Parameters | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | X-Site-ID | header | string | yes | Site/location id. | ## Responses ### 200 — Pending agreements. ```json { "description": "Pending agreements." } ``` ### 401 — ```json { "$ref": "#/components/responses/Error" } ``` ## Schemas in scope Refer to the full OpenAPI spec for resolved `$ref` definitions: - https://docs.hapana-developers.com/openapi/member-v2.json --- Source spec: `docs/openapi/hapana-api.openapi.json` · operationId `list_member_pending_agreements` · version `v2` ## Security schemes ```json { "apiKey": { "type": "http", "scheme": "bearer", "description": "Brand API key: `Authorization: Bearer hk_live_...` (or `hk_test_...` in test mode). Create and rotate keys in Control Center → API Integrations." }, "bearerAuth": { "type": "http", "scheme": "bearer", "bearerFormat": "JWT", "description": "Firebase ID token for `/public-api/v2/member/*` — obtained by signing the member in with your corporate’s Firebase tenant. Passed as `Authorization: Bearer ` alongside the `X-Site-ID` header." } } ``` # GET /public-api/v2/member/policies/pending **Hapana Member API v2** **Base URL**: `https://api.hapana-app.com/public-api/v2/member` ## Summary List policies the member still needs to accept **Tags**: Member API **Authentication**: ```json [ { "bearerAuth": [] } ] ``` ## Parameters | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | X-Site-ID | header | string | yes | Site/location id. | ## Responses ### 200 — Pending policies. ```json { "description": "Pending policies.", "content": { "application/json": { "schema": { "type": "object", "properties": { "object": { "type": "string", "const": "list" }, "data": { "type": "array", "items": { "type": "object", "properties": { "object": { "type": "string", "const": "policy" }, "id": { "type": "string" }, "name": { "type": "string" }, "policyRevisionId": { "type": [ "string", "null" ], "description": "The revision in force; send it back in displayedRevisions when accepting." }, "revisionSha256": { "type": [ "string", "null" ] } } } } } } } } } ``` ### 401 — ```json { "$ref": "#/components/responses/Error" } ``` ## Schemas in scope Refer to the full OpenAPI spec for resolved `$ref` definitions: - https://docs.hapana-developers.com/openapi/member-v2.json --- Source spec: `docs/openapi/hapana-api.openapi.json` · operationId `list_member_pending_policies` · version `v2` ## Security schemes ```json { "apiKey": { "type": "http", "scheme": "bearer", "description": "Brand API key: `Authorization: Bearer hk_live_...` (or `hk_test_...` in test mode). Create and rotate keys in Control Center → API Integrations." }, "bearerAuth": { "type": "http", "scheme": "bearer", "bearerFormat": "JWT", "description": "Firebase ID token for `/public-api/v2/member/*` — obtained by signing the member in with your corporate’s Firebase tenant. Passed as `Authorization: Bearer ` alongside the `X-Site-ID` header." } } ``` # POST /public-api/v2/member/policies/accept **Hapana Member API v2** **Base URL**: `https://api.hapana-app.com/public-api/v2/member` ## Summary Accept one or more site policies **Tags**: Member API **Authentication**: ```json [ { "bearerAuth": [] } ] ``` ## Parameters | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | X-Site-ID | header | string | yes | Site/location id. | ## Request Body ```json { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "policyIds": { "type": "array", "minItems": 1, "items": { "type": "string", "minLength": 1 } }, "displayedRevisions": { "type": "array", "maxItems": 50, "description": "The revision of each policy the member was shown. A revision that is no longer the one in force is refused with 409 POLICY_VERSION_CHANGED. Without it the revision in force is recorded, except within 15 minutes of it replacing different text.", "items": { "type": "object", "properties": { "policyId": { "type": "string", "format": "uuid" }, "policyRevisionId": { "type": "string", "format": "uuid" }, "revisionSha256": { "type": [ "string", "null" ], "minLength": 64, "maxLength": 64 } }, "required": [ "policyId", "policyRevisionId" ] } } }, "required": [ "policyIds" ] } } } } ``` ## Responses ### 201 — Policies accepted. ```json { "description": "Policies accepted." } ``` ### 400 — ```json { "$ref": "#/components/responses/Error" } ``` ### 401 — ```json { "$ref": "#/components/responses/Error" } ``` ### 409 — ```json { "$ref": "#/components/responses/Error" } ``` ### 422 — ```json { "$ref": "#/components/responses/Error" } ``` ## Schemas in scope Refer to the full OpenAPI spec for resolved `$ref` definitions: - https://docs.hapana-developers.com/openapi/member-v2.json --- Source spec: `docs/openapi/hapana-api.openapi.json` · operationId `accept_member_policies` · version `v2` ## Security schemes ```json { "apiKey": { "type": "http", "scheme": "bearer", "description": "Brand API key: `Authorization: Bearer hk_live_...` (or `hk_test_...` in test mode). Create and rotate keys in Control Center → API Integrations." }, "bearerAuth": { "type": "http", "scheme": "bearer", "bearerFormat": "JWT", "description": "Firebase ID token for `/public-api/v2/member/*` — obtained by signing the member in with your corporate’s Firebase tenant. Passed as `Authorization: Bearer ` alongside the `X-Site-ID` header." } } ``` # GET /public-api/v2/member/memberships/{id} **Hapana Member API v2** **Base URL**: `https://api.hapana-app.com/public-api/v2/member` ## Summary Get membership details (incl. earliestCancelDate) **Tags**: Member API **Authentication**: ```json [ { "bearerAuth": [] } ] ``` ## Parameters | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | X-Site-ID | header | string | yes | Site/location id. | | id | path | string | yes | Membership (subscription) id. | ## Responses ### 200 — Membership details. ```json { "description": "Membership details." } ``` ### 401 — ```json { "$ref": "#/components/responses/Error" } ``` ### 404 — ```json { "$ref": "#/components/responses/Error" } ``` ## Schemas in scope Refer to the full OpenAPI spec for resolved `$ref` definitions: - https://docs.hapana-developers.com/openapi/member-v2.json --- Source spec: `docs/openapi/hapana-api.openapi.json` · operationId `get_member_membership` · version `v2` ## Security schemes ```json { "apiKey": { "type": "http", "scheme": "bearer", "description": "Brand API key: `Authorization: Bearer hk_live_...` (or `hk_test_...` in test mode). Create and rotate keys in Control Center → API Integrations." }, "bearerAuth": { "type": "http", "scheme": "bearer", "bearerFormat": "JWT", "description": "Firebase ID token for `/public-api/v2/member/*` — obtained by signing the member in with your corporate’s Firebase tenant. Passed as `Authorization: Bearer ` alongside the `X-Site-ID` header." } } ``` # POST /public-api/v2/member/memberships/{id}/cancel **Hapana Member API v2** **Base URL**: `https://api.hapana-app.com/public-api/v2/member` ## Summary Cancel a membership (immediate, or scheduled via cancelAt) **Tags**: Member API **Authentication**: ```json [ { "bearerAuth": [] } ] ``` ## Parameters | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | X-Site-ID | header | string | yes | Site/location id. | | id | path | string | yes | Membership (subscription) id. | ## Request Body ```json { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "reason": { "type": "string" }, "cancelAt": { "type": "string", "description": "ISO date (YYYY-MM-DD); future = scheduled." } } } } } } ``` ## Responses ### 201 — Cancellation recorded. ```json { "description": "Cancellation recorded." } ``` ### 401 — ```json { "$ref": "#/components/responses/Error" } ``` ### 404 — ```json { "$ref": "#/components/responses/Error" } ``` ### 422 — ```json { "$ref": "#/components/responses/Error" } ``` ## Schemas in scope Refer to the full OpenAPI spec for resolved `$ref` definitions: - https://docs.hapana-developers.com/openapi/member-v2.json --- Source spec: `docs/openapi/hapana-api.openapi.json` · operationId `cancel_member_membership` · version `v2` ## Security schemes ```json { "apiKey": { "type": "http", "scheme": "bearer", "description": "Brand API key: `Authorization: Bearer hk_live_...` (or `hk_test_...` in test mode). Create and rotate keys in Control Center → API Integrations." }, "bearerAuth": { "type": "http", "scheme": "bearer", "bearerFormat": "JWT", "description": "Firebase ID token for `/public-api/v2/member/*` — obtained by signing the member in with your corporate’s Firebase tenant. Passed as `Authorization: Bearer ` alongside the `X-Site-ID` header." } } ``` # GET /public-api/v2/member/payments/methods **Hapana Member API v2** **Base URL**: `https://api.hapana-app.com/public-api/v2/member` ## Summary List payment methods **Tags**: Member API **Authentication**: ```json [ { "bearerAuth": [] } ] ``` ## Parameters | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | X-Site-ID | header | string | yes | Site/location id. | ## Responses ### 200 — Member payment methods. ```json { "description": "Member payment methods." } ``` ### 401 — ```json { "$ref": "#/components/responses/Error" } ``` ## Schemas in scope Refer to the full OpenAPI spec for resolved `$ref` definitions: - https://docs.hapana-developers.com/openapi/member-v2.json --- Source spec: `docs/openapi/hapana-api.openapi.json` · operationId `list_payment_methods` · version `v2` ## Security schemes ```json { "apiKey": { "type": "http", "scheme": "bearer", "description": "Brand API key: `Authorization: Bearer hk_live_...` (or `hk_test_...` in test mode). Create and rotate keys in Control Center → API Integrations." }, "bearerAuth": { "type": "http", "scheme": "bearer", "bearerFormat": "JWT", "description": "Firebase ID token for `/public-api/v2/member/*` — obtained by signing the member in with your corporate’s Firebase tenant. Passed as `Authorization: Bearer ` alongside the `X-Site-ID` header." } } ``` # POST /public-api/v2/member/payments/methods **Hapana Member API v2** **Base URL**: `https://api.hapana-app.com/public-api/v2/member` ## Summary Initiate adding a payment method (returns Stripe SetupIntent) **Tags**: Member API **Authentication**: ```json [ { "bearerAuth": [] } ] ``` ## Parameters | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | X-Site-ID | header | string | yes | Site/location id. | ## Responses ### 200 — SetupIntent details: `setupIntentClientSecret` plus the site’s `publishableKey` to confirm it with Stripe.js / Elements. ```json { "description": "SetupIntent details: `setupIntentClientSecret` plus the site’s `publishableKey` to confirm it with Stripe.js / Elements." } ``` ### 401 — ```json { "$ref": "#/components/responses/Error" } ``` ## Schemas in scope Refer to the full OpenAPI spec for resolved `$ref` definitions: - https://docs.hapana-developers.com/openapi/member-v2.json --- Source spec: `docs/openapi/hapana-api.openapi.json` · operationId `initiate_payment_method` · version `v2` ## Security schemes ```json { "apiKey": { "type": "http", "scheme": "bearer", "description": "Brand API key: `Authorization: Bearer hk_live_...` (or `hk_test_...` in test mode). Create and rotate keys in Control Center → API Integrations." }, "bearerAuth": { "type": "http", "scheme": "bearer", "bearerFormat": "JWT", "description": "Firebase ID token for `/public-api/v2/member/*` — obtained by signing the member in with your corporate’s Firebase tenant. Passed as `Authorization: Bearer ` alongside the `X-Site-ID` header." } } ``` # POST /public-api/v2/member/payments/methods/{id}/complete **Hapana Member API v2** **Base URL**: `https://api.hapana-app.com/public-api/v2/member` ## Summary Complete payment method setup after Stripe confirmation **Tags**: Member API **Authentication**: ```json [ { "bearerAuth": [] } ] ``` ## Parameters | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | X-Site-ID | header | string | yes | Site/location id. | | id | path | string | yes | Payment method id. | ## Responses ### 200 — Payment method activated. ```json { "description": "Payment method activated." } ``` ### 401 — ```json { "$ref": "#/components/responses/Error" } ``` ### 404 — ```json { "$ref": "#/components/responses/Error" } ``` ## Schemas in scope Refer to the full OpenAPI spec for resolved `$ref` definitions: - https://docs.hapana-developers.com/openapi/member-v2.json --- Source spec: `docs/openapi/hapana-api.openapi.json` · operationId `complete_payment_method` · version `v2` ## Security schemes ```json { "apiKey": { "type": "http", "scheme": "bearer", "description": "Brand API key: `Authorization: Bearer hk_live_...` (or `hk_test_...` in test mode). Create and rotate keys in Control Center → API Integrations." }, "bearerAuth": { "type": "http", "scheme": "bearer", "bearerFormat": "JWT", "description": "Firebase ID token for `/public-api/v2/member/*` — obtained by signing the member in with your corporate’s Firebase tenant. Passed as `Authorization: Bearer ` alongside the `X-Site-ID` header." } } ``` # DELETE /public-api/v2/member/payments/methods/{id} **Hapana Member API v2** **Base URL**: `https://api.hapana-app.com/public-api/v2/member` ## Summary Remove a payment method **Tags**: Member API **Authentication**: ```json [ { "bearerAuth": [] } ] ``` ## Parameters | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | X-Site-ID | header | string | yes | Site/location id. | | id | path | string | yes | Payment method id. | ## Responses ### 204 — Payment method removed. ```json { "description": "Payment method removed." } ``` ### 401 — ```json { "$ref": "#/components/responses/Error" } ``` ### 404 — ```json { "$ref": "#/components/responses/Error" } ``` ## Schemas in scope Refer to the full OpenAPI spec for resolved `$ref` definitions: - https://docs.hapana-developers.com/openapi/member-v2.json --- Source spec: `docs/openapi/hapana-api.openapi.json` · operationId `delete_payment_method` · version `v2` ## Security schemes ```json { "apiKey": { "type": "http", "scheme": "bearer", "description": "Brand API key: `Authorization: Bearer hk_live_...` (or `hk_test_...` in test mode). Create and rotate keys in Control Center → API Integrations." }, "bearerAuth": { "type": "http", "scheme": "bearer", "bearerFormat": "JWT", "description": "Firebase ID token for `/public-api/v2/member/*` — obtained by signing the member in with your corporate’s Firebase tenant. Passed as `Authorization: Bearer ` alongside the `X-Site-ID` header." } } ``` # GET /public-api/v2/member/payments/history **Hapana Member API v2** **Base URL**: `https://api.hapana-app.com/public-api/v2/member` ## Summary Get billing/payment history **Tags**: Member API **Authentication**: ```json [ { "bearerAuth": [] } ] ``` ## Parameters | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | X-Site-ID | header | string | yes | Site/location id. | | status | query | string | no | Filter by status (comma-separated). | | startDate | query | string | no | Start date. | | endDate | query | string | no | End date. | | limit | query | integer | no | Max results. | ## Responses ### 200 — Payment history. ```json { "description": "Payment history." } ``` ### 401 — ```json { "$ref": "#/components/responses/Error" } ``` ## Schemas in scope Refer to the full OpenAPI spec for resolved `$ref` definitions: - https://docs.hapana-developers.com/openapi/member-v2.json --- Source spec: `docs/openapi/hapana-api.openapi.json` · operationId `get_payment_history` · version `v2` ## Security schemes ```json { "apiKey": { "type": "http", "scheme": "bearer", "description": "Brand API key: `Authorization: Bearer hk_live_...` (or `hk_test_...` in test mode). Create and rotate keys in Control Center → API Integrations." }, "bearerAuth": { "type": "http", "scheme": "bearer", "bearerFormat": "JWT", "description": "Firebase ID token for `/public-api/v2/member/*` — obtained by signing the member in with your corporate’s Firebase tenant. Passed as `Authorization: Bearer ` alongside the `X-Site-ID` header." } } ``` # GET /public-api/v2/member/waitlist **Hapana Member API v2** **Base URL**: `https://api.hapana-app.com/public-api/v2/member` ## Summary List the member's waitlist entries **Tags**: Member API **Authentication**: ```json [ { "bearerAuth": [] } ] ``` ## Parameters | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | X-Site-ID | header | string | yes | Site/location id. | ## Responses ### 200 — Active waitlist entries (with position). ```json { "description": "Active waitlist entries (with position)." } ``` ### 401 — ```json { "$ref": "#/components/responses/Error" } ``` ## Schemas in scope Refer to the full OpenAPI spec for resolved `$ref` definitions: - https://docs.hapana-developers.com/openapi/member-v2.json --- Source spec: `docs/openapi/hapana-api.openapi.json` · operationId `list_member_waitlist` · version `v2` ## Security schemes ```json { "apiKey": { "type": "http", "scheme": "bearer", "description": "Brand API key: `Authorization: Bearer hk_live_...` (or `hk_test_...` in test mode). Create and rotate keys in Control Center → API Integrations." }, "bearerAuth": { "type": "http", "scheme": "bearer", "bearerFormat": "JWT", "description": "Firebase ID token for `/public-api/v2/member/*` — obtained by signing the member in with your corporate’s Firebase tenant. Passed as `Authorization: Bearer ` alongside the `X-Site-ID` header." } } ``` # POST /public-api/v2/member/waitlist **Hapana Member API v2** **Base URL**: `https://api.hapana-app.com/public-api/v2/member` ## Summary Join a session waitlist **Tags**: Member API **Authentication**: ```json [ { "bearerAuth": [] } ] ``` ## Parameters | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | X-Site-ID | header | string | yes | Site/location id. | ## Request Body ```json { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "sessionId": { "type": "string" } }, "required": [ "sessionId" ] } } } } ``` ## Responses ### 201 — Waitlist entry created (with position). ```json { "description": "Waitlist entry created (with position)." } ``` ### 400 — ```json { "$ref": "#/components/responses/Error" } ``` ### 401 — ```json { "$ref": "#/components/responses/Error" } ``` ### 422 — ```json { "$ref": "#/components/responses/Error" } ``` ## Schemas in scope Refer to the full OpenAPI spec for resolved `$ref` definitions: - https://docs.hapana-developers.com/openapi/member-v2.json --- Source spec: `docs/openapi/hapana-api.openapi.json` · operationId `join_member_waitlist` · version `v2` ## Security schemes ```json { "apiKey": { "type": "http", "scheme": "bearer", "description": "Brand API key: `Authorization: Bearer hk_live_...` (or `hk_test_...` in test mode). Create and rotate keys in Control Center → API Integrations." }, "bearerAuth": { "type": "http", "scheme": "bearer", "bearerFormat": "JWT", "description": "Firebase ID token for `/public-api/v2/member/*` — obtained by signing the member in with your corporate’s Firebase tenant. Passed as `Authorization: Bearer ` alongside the `X-Site-ID` header." } } ``` # DELETE /public-api/v2/member/waitlist/{id} **Hapana Member API v2** **Base URL**: `https://api.hapana-app.com/public-api/v2/member` ## Summary Leave a waitlist **Tags**: Member API **Authentication**: ```json [ { "bearerAuth": [] } ] ``` ## Parameters | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | X-Site-ID | header | string | yes | Site/location id. | | id | path | string | yes | Waitlist entry id. | ## Responses ### 204 — Left the waitlist. ```json { "description": "Left the waitlist." } ``` ### 401 — ```json { "$ref": "#/components/responses/Error" } ``` ### 404 — ```json { "$ref": "#/components/responses/Error" } ``` ## Schemas in scope Refer to the full OpenAPI spec for resolved `$ref` definitions: - https://docs.hapana-developers.com/openapi/member-v2.json --- Source spec: `docs/openapi/hapana-api.openapi.json` · operationId `leave_member_waitlist` · version `v2` ## Security schemes ```json { "apiKey": { "type": "http", "scheme": "bearer", "description": "Brand API key: `Authorization: Bearer hk_live_...` (or `hk_test_...` in test mode). Create and rotate keys in Control Center → API Integrations." }, "bearerAuth": { "type": "http", "scheme": "bearer", "bearerFormat": "JWT", "description": "Firebase ID token for `/public-api/v2/member/*` — obtained by signing the member in with your corporate’s Firebase tenant. Passed as `Authorization: Bearer ` alongside the `X-Site-ID` header." } } ``` # GET /public-api/v2/member/sites/{id} **Hapana Member API v2** **Base URL**: `https://api.hapana-app.com/public-api/v2/member` ## Summary Get member-safe site details **Tags**: Member API **Authentication**: ```json [ { "bearerAuth": [] } ] ``` ## Parameters | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | X-Site-ID | header | string | yes | Site/location id. | | id | path | string | yes | Site/location id. | ## Responses ### 200 — Member-safe site details. ```json { "description": "Member-safe site details." } ``` ### 401 — ```json { "$ref": "#/components/responses/Error" } ``` ### 404 — ```json { "$ref": "#/components/responses/Error" } ``` ## Schemas in scope Refer to the full OpenAPI spec for resolved `$ref` definitions: - https://docs.hapana-developers.com/openapi/member-v2.json --- Source spec: `docs/openapi/hapana-api.openapi.json` · operationId `get_member_site` · version `v2` ## Security schemes ```json { "apiKey": { "type": "http", "scheme": "bearer", "description": "Brand API key: `Authorization: Bearer hk_live_...` (or `hk_test_...` in test mode). Create and rotate keys in Control Center → API Integrations." }, "bearerAuth": { "type": "http", "scheme": "bearer", "bearerFormat": "JWT", "description": "Firebase ID token for `/public-api/v2/member/*` — obtained by signing the member in with your corporate’s Firebase tenant. Passed as `Authorization: Bearer ` alongside the `X-Site-ID` header." } } ``` --- # Hapana Business API ## v2 # GET /public-api/v2/sites **Hapana Business API v2** **Base URL**: `https://public-api.hapana-app.com/public-api/v2` ## Summary List sites ## Description Every location in your brand. **Required scope:** `sites:read` **Tags**: Sites **Authentication**: ```json [ { "partnerKey": [] } ] ``` ## Responses ### 200 — Success ```json { "description": "Success", "content": { "application/json": { "schema": { "type": "object", "properties": { "object": { "type": "string", "enum": [ "list" ] }, "data": { "type": "array", "items": { "type": "object", "properties": { "object": { "type": "string", "const": "site" }, "id": { "type": "string", "format": "uuid", "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$" }, "name": { "type": "string" }, "status": { "type": "string", "enum": [ "active", "inactive" ] }, "isDigital": { "type": "boolean" }, "addressStreet": { "anyOf": [ { "type": "string" }, { "type": "null" } ] }, "addressCity": { "anyOf": [ { "type": "string" }, { "type": "null" } ] }, "addressState": { "anyOf": [ { "type": "string" }, { "type": "null" } ] }, "addressPostalCode": { "anyOf": [ { "type": "string" }, { "type": "null" } ] }, "addressCountry": { "anyOf": [ { "type": "string" }, { "type": "null" } ] }, "latitude": { "anyOf": [ { "type": "number" }, { "type": "null" } ] }, "longitude": { "anyOf": [ { "type": "number" }, { "type": "null" } ] }, "regionName": { "anyOf": [ { "type": "string" }, { "type": "null" } ] }, "timezone": { "anyOf": [ { "type": "string" }, { "type": "null" } ] }, "openingHours": { "anyOf": [ { "type": "object", "properties": { "everyDay": { "anyOf": [ { "type": "object", "properties": { "open": { "type": "string" }, "close": { "type": "string" } }, "required": [ "open", "close" ] }, { "type": "null" } ] }, "byDay": { "anyOf": [ { "type": "object", "properties": { "monday": { "anyOf": [ { "type": "object", "properties": { "open": { "type": "string" }, "close": { "type": "string" } }, "required": [ "open", "close" ] }, { "type": "null" } ] }, "tuesday": { "anyOf": [ { "type": "object", "properties": { "open": { "type": "string" }, "close": { "type": "string" } }, "required": [ "open", "close" ] }, { "type": "null" } ] }, "wednesday": { "anyOf": [ { "type": "object", "properties": { "open": { "type": "string" }, "close": { "type": "string" } }, "required": [ "open", "close" ] }, { "type": "null" } ] }, "thursday": { "anyOf": [ { "type": "object", "properties": { "open": { "type": "string" }, "close": { "type": "string" } }, "required": [ "open", "close" ] }, { "type": "null" } ] }, "friday": { "anyOf": [ { "type": "object", "properties": { "open": { "type": "string" }, "close": { "type": "string" } }, "required": [ "open", "close" ] }, { "type": "null" } ] }, "saturday": { "anyOf": [ { "type": "object", "properties": { "open": { "type": "string" }, "close": { "type": "string" } }, "required": [ "open", "close" ] }, { "type": "null" } ] }, "sunday": { "anyOf": [ { "type": "object", "properties": { "open": { "type": "string" }, "close": { "type": "string" } }, "required": [ "open", "close" ] }, { "type": "null" } ] } }, "required": [ "monday", "tuesday", "wednesday", "thursday", "friday", "saturday", "sunday" ] }, { "type": "null" } ] } }, "required": [ "everyDay", "byDay" ] }, { "type": "null" } ] }, "legacyId": { "anyOf": [ { "type": "string" }, { "type": "null" } ] }, "createdAt": { "type": "string", "format": "date-time", "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$" } }, "required": [ "object", "id", "name", "status", "isDigital", "addressStreet", "addressCity", "addressState", "addressPostalCode", "addressCountry", "latitude", "longitude", "regionName", "timezone", "openingHours", "legacyId", "createdAt" ] } }, "hasMore": { "type": "boolean" }, "nextCursor": { "type": [ "string", "null" ] }, "url": { "type": "string" } }, "required": [ "object", "data", "hasMore", "url" ] } } } } ``` ### 400 — Malformed request ```json { "description": "Malformed request", "content": { "application/json": { "schema": { "type": "object", "properties": { "error": { "type": "string" }, "code": { "type": "string" }, "message": { "type": "string" } }, "required": [ "error" ] } } } } ``` ### 403 — The API key does not have the required scope ```json { "description": "The API key does not have the required scope", "content": { "application/json": { "schema": { "type": "object", "properties": { "error": { "type": "string" }, "code": { "type": "string" }, "message": { "type": "string" } }, "required": [ "error" ] } } } } ``` ### 404 — Not found. A record that belongs to another brand also returns 404. ```json { "description": "Not found. A record that belongs to another brand also returns 404.", "content": { "application/json": { "schema": { "type": "object", "properties": { "error": { "type": "string" }, "code": { "type": "string" }, "message": { "type": "string" } }, "required": [ "error" ] } } } } ``` ## Schemas in scope Refer to the full OpenAPI spec for resolved `$ref` definitions: - https://docs.hapana-developers.com/openapi/business-v2.json --- Source spec: `docs/openapi/hapana-business-api.openapi.json` · operationId `get_sites` · version `v2` ## Security schemes ```json { "partnerKey": { "type": "http", "scheme": "bearer", "description": "The brand API key, e.g. `hk_test_…`. Paste the value only, without `Bearer`." } } ``` # POST /public-api/v2/sites **Hapana Business API v2** **Base URL**: `https://public-api.hapana-app.com/public-api/v2` ## Summary Create a site ## Description Creates a location. Only the fields documented below are accepted; any other field is ignored. **Required scope:** `sites:write` **Tags**: Sites **Authentication**: ```json [ { "partnerKey": [] } ] ``` ## Parameters | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | Idempotency-Key | header | string | no | Makes the request safe to retry. A retry with the same key returns the first response; reusing a key with a different body is refused with 422. | ## Request Body ```json { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "name": { "type": "string", "minLength": 1, "maxLength": 255 }, "workshopType": { "type": "string", "enum": [ "fitness", "yoga", "crossfit", "martial_arts", "swimming", "pilates", "dance", "cycling" ] }, "addressStreet": { "type": "string", "maxLength": 255 }, "addressCity": { "type": "string", "maxLength": 255 }, "addressState": { "type": "string", "maxLength": 255 }, "addressPostalCode": { "type": "string", "maxLength": 32 }, "addressCountry": { "type": "string", "maxLength": 255 }, "timezone": { "type": "string", "maxLength": 64 }, "phone": { "type": "string", "maxLength": 64 }, "email": { "type": "string", "format": "email", "pattern": "^(?!\\.)(?!.*\\.\\.)([A-Za-z0-9_'+\\-\\.]*)[A-Za-z0-9_+-]@([A-Za-z0-9][A-Za-z0-9\\-]*\\.)+[A-Za-z]{2,}$" } }, "required": [ "name", "workshopType" ] } } } } ``` ## Responses ### 201 — Success ```json { "description": "Success", "content": { "application/json": { "schema": { "type": "object", "properties": { "object": { "type": "string", "const": "site" }, "id": { "type": "string", "format": "uuid", "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$" }, "name": { "type": "string" }, "status": { "type": "string", "enum": [ "active", "inactive" ] }, "isDigital": { "type": "boolean" }, "addressStreet": { "anyOf": [ { "type": "string" }, { "type": "null" } ] }, "addressCity": { "anyOf": [ { "type": "string" }, { "type": "null" } ] }, "addressState": { "anyOf": [ { "type": "string" }, { "type": "null" } ] }, "addressPostalCode": { "anyOf": [ { "type": "string" }, { "type": "null" } ] }, "addressCountry": { "anyOf": [ { "type": "string" }, { "type": "null" } ] }, "latitude": { "anyOf": [ { "type": "number" }, { "type": "null" } ] }, "longitude": { "anyOf": [ { "type": "number" }, { "type": "null" } ] }, "regionName": { "anyOf": [ { "type": "string" }, { "type": "null" } ] }, "timezone": { "anyOf": [ { "type": "string" }, { "type": "null" } ] }, "openingHours": { "anyOf": [ { "type": "object", "properties": { "everyDay": { "anyOf": [ { "type": "object", "properties": { "open": { "type": "string" }, "close": { "type": "string" } }, "required": [ "open", "close" ] }, { "type": "null" } ] }, "byDay": { "anyOf": [ { "type": "object", "properties": { "monday": { "anyOf": [ { "type": "object", "properties": { "open": { "type": "string" }, "close": { "type": "string" } }, "required": [ "open", "close" ] }, { "type": "null" } ] }, "tuesday": { "anyOf": [ { "type": "object", "properties": { "open": { "type": "string" }, "close": { "type": "string" } }, "required": [ "open", "close" ] }, { "type": "null" } ] }, "wednesday": { "anyOf": [ { "type": "object", "properties": { "open": { "type": "string" }, "close": { "type": "string" } }, "required": [ "open", "close" ] }, { "type": "null" } ] }, "thursday": { "anyOf": [ { "type": "object", "properties": { "open": { "type": "string" }, "close": { "type": "string" } }, "required": [ "open", "close" ] }, { "type": "null" } ] }, "friday": { "anyOf": [ { "type": "object", "properties": { "open": { "type": "string" }, "close": { "type": "string" } }, "required": [ "open", "close" ] }, { "type": "null" } ] }, "saturday": { "anyOf": [ { "type": "object", "properties": { "open": { "type": "string" }, "close": { "type": "string" } }, "required": [ "open", "close" ] }, { "type": "null" } ] }, "sunday": { "anyOf": [ { "type": "object", "properties": { "open": { "type": "string" }, "close": { "type": "string" } }, "required": [ "open", "close" ] }, { "type": "null" } ] } }, "required": [ "monday", "tuesday", "wednesday", "thursday", "friday", "saturday", "sunday" ] }, { "type": "null" } ] } }, "required": [ "everyDay", "byDay" ] }, { "type": "null" } ] }, "legacyId": { "anyOf": [ { "type": "string" }, { "type": "null" } ] }, "createdAt": { "type": "string", "format": "date-time", "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$" } }, "required": [ "object", "id", "name", "status", "isDigital", "addressStreet", "addressCity", "addressState", "addressPostalCode", "addressCountry", "latitude", "longitude", "regionName", "timezone", "openingHours", "legacyId", "createdAt" ] } } } } ``` ### 400 — Malformed request ```json { "description": "Malformed request", "content": { "application/json": { "schema": { "type": "object", "properties": { "error": { "type": "string" }, "code": { "type": "string" }, "message": { "type": "string" } }, "required": [ "error" ] } } } } ``` ### 403 — The API key does not have the required scope ```json { "description": "The API key does not have the required scope", "content": { "application/json": { "schema": { "type": "object", "properties": { "error": { "type": "string" }, "code": { "type": "string" }, "message": { "type": "string" } }, "required": [ "error" ] } } } } ``` ### 404 — Not found. A record that belongs to another brand also returns 404. ```json { "description": "Not found. A record that belongs to another brand also returns 404.", "content": { "application/json": { "schema": { "type": "object", "properties": { "error": { "type": "string" }, "code": { "type": "string" }, "message": { "type": "string" } }, "required": [ "error" ] } } } } ``` ### 409 — A request with this Idempotency-Key is still being processed ```json { "description": "A request with this Idempotency-Key is still being processed" } ``` ### 422 — Idempotency-Key reused with a different request body ```json { "description": "Idempotency-Key reused with a different request body" } ``` ## Schemas in scope Refer to the full OpenAPI spec for resolved `$ref` definitions: - https://docs.hapana-developers.com/openapi/business-v2.json --- Source spec: `docs/openapi/hapana-business-api.openapi.json` · operationId `post_sites` · version `v2` ## Security schemes ```json { "partnerKey": { "type": "http", "scheme": "bearer", "description": "The brand API key, e.g. `hk_test_…`. Paste the value only, without `Bearer`." } } ``` # GET /public-api/v2/sites/{id} **Hapana Business API v2** **Base URL**: `https://public-api.hapana-app.com/public-api/v2` ## Summary Retrieve a site ## Description **Required scope:** `sites:read` **Tags**: Sites **Authentication**: ```json [ { "partnerKey": [] } ] ``` ## Parameters | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | id | path | string | yes | | ## Responses ### 200 — Success ```json { "description": "Success", "content": { "application/json": { "schema": { "type": "object", "properties": { "object": { "type": "string", "const": "site" }, "id": { "type": "string", "format": "uuid", "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$" }, "name": { "type": "string" }, "status": { "type": "string", "enum": [ "active", "inactive" ] }, "isDigital": { "type": "boolean" }, "addressStreet": { "anyOf": [ { "type": "string" }, { "type": "null" } ] }, "addressCity": { "anyOf": [ { "type": "string" }, { "type": "null" } ] }, "addressState": { "anyOf": [ { "type": "string" }, { "type": "null" } ] }, "addressPostalCode": { "anyOf": [ { "type": "string" }, { "type": "null" } ] }, "addressCountry": { "anyOf": [ { "type": "string" }, { "type": "null" } ] }, "latitude": { "anyOf": [ { "type": "number" }, { "type": "null" } ] }, "longitude": { "anyOf": [ { "type": "number" }, { "type": "null" } ] }, "regionName": { "anyOf": [ { "type": "string" }, { "type": "null" } ] }, "timezone": { "anyOf": [ { "type": "string" }, { "type": "null" } ] }, "openingHours": { "anyOf": [ { "type": "object", "properties": { "everyDay": { "anyOf": [ { "type": "object", "properties": { "open": { "type": "string" }, "close": { "type": "string" } }, "required": [ "open", "close" ] }, { "type": "null" } ] }, "byDay": { "anyOf": [ { "type": "object", "properties": { "monday": { "anyOf": [ { "type": "object", "properties": { "open": { "type": "string" }, "close": { "type": "string" } }, "required": [ "open", "close" ] }, { "type": "null" } ] }, "tuesday": { "anyOf": [ { "type": "object", "properties": { "open": { "type": "string" }, "close": { "type": "string" } }, "required": [ "open", "close" ] }, { "type": "null" } ] }, "wednesday": { "anyOf": [ { "type": "object", "properties": { "open": { "type": "string" }, "close": { "type": "string" } }, "required": [ "open", "close" ] }, { "type": "null" } ] }, "thursday": { "anyOf": [ { "type": "object", "properties": { "open": { "type": "string" }, "close": { "type": "string" } }, "required": [ "open", "close" ] }, { "type": "null" } ] }, "friday": { "anyOf": [ { "type": "object", "properties": { "open": { "type": "string" }, "close": { "type": "string" } }, "required": [ "open", "close" ] }, { "type": "null" } ] }, "saturday": { "anyOf": [ { "type": "object", "properties": { "open": { "type": "string" }, "close": { "type": "string" } }, "required": [ "open", "close" ] }, { "type": "null" } ] }, "sunday": { "anyOf": [ { "type": "object", "properties": { "open": { "type": "string" }, "close": { "type": "string" } }, "required": [ "open", "close" ] }, { "type": "null" } ] } }, "required": [ "monday", "tuesday", "wednesday", "thursday", "friday", "saturday", "sunday" ] }, { "type": "null" } ] } }, "required": [ "everyDay", "byDay" ] }, { "type": "null" } ] }, "legacyId": { "anyOf": [ { "type": "string" }, { "type": "null" } ] }, "createdAt": { "type": "string", "format": "date-time", "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$" } }, "required": [ "object", "id", "name", "status", "isDigital", "addressStreet", "addressCity", "addressState", "addressPostalCode", "addressCountry", "latitude", "longitude", "regionName", "timezone", "openingHours", "legacyId", "createdAt" ] } } } } ``` ### 400 — Malformed request ```json { "description": "Malformed request", "content": { "application/json": { "schema": { "type": "object", "properties": { "error": { "type": "string" }, "code": { "type": "string" }, "message": { "type": "string" } }, "required": [ "error" ] } } } } ``` ### 403 — The API key does not have the required scope ```json { "description": "The API key does not have the required scope", "content": { "application/json": { "schema": { "type": "object", "properties": { "error": { "type": "string" }, "code": { "type": "string" }, "message": { "type": "string" } }, "required": [ "error" ] } } } } ``` ### 404 — Not found. A record that belongs to another brand also returns 404. ```json { "description": "Not found. A record that belongs to another brand also returns 404.", "content": { "application/json": { "schema": { "type": "object", "properties": { "error": { "type": "string" }, "code": { "type": "string" }, "message": { "type": "string" } }, "required": [ "error" ] } } } } ``` ## Schemas in scope Refer to the full OpenAPI spec for resolved `$ref` definitions: - https://docs.hapana-developers.com/openapi/business-v2.json --- Source spec: `docs/openapi/hapana-business-api.openapi.json` · operationId `get_sites__id_` · version `v2` ## Security schemes ```json { "partnerKey": { "type": "http", "scheme": "bearer", "description": "The brand API key, e.g. `hk_test_…`. Paste the value only, without `Bearer`." } } ``` # PATCH /public-api/v2/sites/{id} **Hapana Business API v2** **Base URL**: `https://public-api.hapana-app.com/public-api/v2` ## Summary Update a site ## Description **Required scope:** `sites:write` **Tags**: Sites **Authentication**: ```json [ { "partnerKey": [] } ] ``` ## Parameters | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | id | path | string | yes | | | Idempotency-Key | header | string | no | Makes the request safe to retry. A retry with the same key returns the first response; reusing a key with a different body is refused with 422. | ## Request Body ```json { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "name": { "type": "string", "minLength": 1, "maxLength": 255 }, "workshopType": { "type": "string", "enum": [ "fitness", "yoga", "crossfit", "martial_arts", "swimming", "pilates", "dance", "cycling" ] }, "addressStreet": { "type": "string", "maxLength": 255 }, "addressCity": { "type": "string", "maxLength": 255 }, "addressState": { "type": "string", "maxLength": 255 }, "addressPostalCode": { "type": "string", "maxLength": 32 }, "addressCountry": { "type": "string", "maxLength": 255 }, "timezone": { "type": "string", "maxLength": 64 }, "phone": { "type": "string", "maxLength": 64 }, "email": { "type": "string", "format": "email", "pattern": "^(?!\\.)(?!.*\\.\\.)([A-Za-z0-9_'+\\-\\.]*)[A-Za-z0-9_+-]@([A-Za-z0-9][A-Za-z0-9\\-]*\\.)+[A-Za-z]{2,}$" } } } } } } ``` ## Responses ### 200 — Success ```json { "description": "Success", "content": { "application/json": { "schema": { "type": "object", "properties": { "object": { "type": "string", "const": "site" }, "id": { "type": "string", "format": "uuid", "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$" }, "name": { "type": "string" }, "status": { "type": "string", "enum": [ "active", "inactive" ] }, "isDigital": { "type": "boolean" }, "addressStreet": { "anyOf": [ { "type": "string" }, { "type": "null" } ] }, "addressCity": { "anyOf": [ { "type": "string" }, { "type": "null" } ] }, "addressState": { "anyOf": [ { "type": "string" }, { "type": "null" } ] }, "addressPostalCode": { "anyOf": [ { "type": "string" }, { "type": "null" } ] }, "addressCountry": { "anyOf": [ { "type": "string" }, { "type": "null" } ] }, "latitude": { "anyOf": [ { "type": "number" }, { "type": "null" } ] }, "longitude": { "anyOf": [ { "type": "number" }, { "type": "null" } ] }, "regionName": { "anyOf": [ { "type": "string" }, { "type": "null" } ] }, "timezone": { "anyOf": [ { "type": "string" }, { "type": "null" } ] }, "openingHours": { "anyOf": [ { "type": "object", "properties": { "everyDay": { "anyOf": [ { "type": "object", "properties": { "open": { "type": "string" }, "close": { "type": "string" } }, "required": [ "open", "close" ] }, { "type": "null" } ] }, "byDay": { "anyOf": [ { "type": "object", "properties": { "monday": { "anyOf": [ { "type": "object", "properties": { "open": { "type": "string" }, "close": { "type": "string" } }, "required": [ "open", "close" ] }, { "type": "null" } ] }, "tuesday": { "anyOf": [ { "type": "object", "properties": { "open": { "type": "string" }, "close": { "type": "string" } }, "required": [ "open", "close" ] }, { "type": "null" } ] }, "wednesday": { "anyOf": [ { "type": "object", "properties": { "open": { "type": "string" }, "close": { "type": "string" } }, "required": [ "open", "close" ] }, { "type": "null" } ] }, "thursday": { "anyOf": [ { "type": "object", "properties": { "open": { "type": "string" }, "close": { "type": "string" } }, "required": [ "open", "close" ] }, { "type": "null" } ] }, "friday": { "anyOf": [ { "type": "object", "properties": { "open": { "type": "string" }, "close": { "type": "string" } }, "required": [ "open", "close" ] }, { "type": "null" } ] }, "saturday": { "anyOf": [ { "type": "object", "properties": { "open": { "type": "string" }, "close": { "type": "string" } }, "required": [ "open", "close" ] }, { "type": "null" } ] }, "sunday": { "anyOf": [ { "type": "object", "properties": { "open": { "type": "string" }, "close": { "type": "string" } }, "required": [ "open", "close" ] }, { "type": "null" } ] } }, "required": [ "monday", "tuesday", "wednesday", "thursday", "friday", "saturday", "sunday" ] }, { "type": "null" } ] } }, "required": [ "everyDay", "byDay" ] }, { "type": "null" } ] }, "legacyId": { "anyOf": [ { "type": "string" }, { "type": "null" } ] }, "createdAt": { "type": "string", "format": "date-time", "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$" } }, "required": [ "object", "id", "name", "status", "isDigital", "addressStreet", "addressCity", "addressState", "addressPostalCode", "addressCountry", "latitude", "longitude", "regionName", "timezone", "openingHours", "legacyId", "createdAt" ] } } } } ``` ### 400 — Malformed request ```json { "description": "Malformed request", "content": { "application/json": { "schema": { "type": "object", "properties": { "error": { "type": "string" }, "code": { "type": "string" }, "message": { "type": "string" } }, "required": [ "error" ] } } } } ``` ### 403 — The API key does not have the required scope ```json { "description": "The API key does not have the required scope", "content": { "application/json": { "schema": { "type": "object", "properties": { "error": { "type": "string" }, "code": { "type": "string" }, "message": { "type": "string" } }, "required": [ "error" ] } } } } ``` ### 404 — Not found. A record that belongs to another brand also returns 404. ```json { "description": "Not found. A record that belongs to another brand also returns 404.", "content": { "application/json": { "schema": { "type": "object", "properties": { "error": { "type": "string" }, "code": { "type": "string" }, "message": { "type": "string" } }, "required": [ "error" ] } } } } ``` ### 409 — A request with this Idempotency-Key is still being processed ```json { "description": "A request with this Idempotency-Key is still being processed" } ``` ### 422 — Idempotency-Key reused with a different request body ```json { "description": "Idempotency-Key reused with a different request body" } ``` ## Schemas in scope Refer to the full OpenAPI spec for resolved `$ref` definitions: - https://docs.hapana-developers.com/openapi/business-v2.json --- Source spec: `docs/openapi/hapana-business-api.openapi.json` · operationId `patch_sites__id_` · version `v2` ## Security schemes ```json { "partnerKey": { "type": "http", "scheme": "bearer", "description": "The brand API key, e.g. `hk_test_…`. Paste the value only, without `Bearer`." } } ``` # GET /public-api/v2/staff **Hapana Business API v2** **Base URL**: `https://public-api.hapana-app.com/public-api/v2` ## Summary List staff ## Description **Required scope:** `staff:read` **Tags**: Staff **Authentication**: ```json [ { "partnerKey": [] } ] ``` ## Parameters | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | cursor | query | string | no | | | limit | query | integer | no | | | siteId | query | string | no | | | status | query | string | no | | | instructor | query | string | no | | | updatedSince | query | string | no | | ## Responses ### 200 — Success ```json { "description": "Success", "content": { "application/json": { "schema": { "type": "object", "properties": { "object": { "type": "string", "enum": [ "list" ] }, "data": { "type": "array", "items": { "type": "object", "properties": { "object": { "type": "string", "const": "staff" }, "id": { "type": "string", "format": "uuid", "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$" }, "firstName": { "type": "string" }, "lastName": { "type": "string" }, "email": { "anyOf": [ { "type": "string" }, { "type": "null" } ] }, "phone": { "anyOf": [ { "type": "string" }, { "type": "null" } ] }, "status": { "type": "string", "enum": [ "active", "inactive" ] }, "employeeNumber": { "anyOf": [ { "type": "string" }, { "type": "null" } ] }, "instructor": { "type": "boolean" }, "photoUrl": { "anyOf": [ { "type": "string" }, { "type": "null" } ] }, "bio": { "anyOf": [ { "type": "string" }, { "type": "null" } ] }, "siteIds": { "type": "array", "items": { "type": "string", "format": "uuid", "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$" } }, "homeSiteId": { "anyOf": [ { "type": "string", "format": "uuid", "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$" }, { "type": "null" } ] }, "timezone": { "anyOf": [ { "type": "string" }, { "type": "null" } ] }, "workingHours": { "anyOf": [ { "type": "object", "properties": { "siteId": { "type": "string", "format": "uuid", "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$" }, "monday": { "anyOf": [ { "type": "object", "properties": { "startTime": { "type": "string" }, "endTime": { "type": "string" } }, "required": [ "startTime", "endTime" ] }, { "type": "null" } ] }, "tuesday": { "anyOf": [ { "type": "object", "properties": { "startTime": { "type": "string" }, "endTime": { "type": "string" } }, "required": [ "startTime", "endTime" ] }, { "type": "null" } ] }, "wednesday": { "anyOf": [ { "type": "object", "properties": { "startTime": { "type": "string" }, "endTime": { "type": "string" } }, "required": [ "startTime", "endTime" ] }, { "type": "null" } ] }, "thursday": { "anyOf": [ { "type": "object", "properties": { "startTime": { "type": "string" }, "endTime": { "type": "string" } }, "required": [ "startTime", "endTime" ] }, { "type": "null" } ] }, "friday": { "anyOf": [ { "type": "object", "properties": { "startTime": { "type": "string" }, "endTime": { "type": "string" } }, "required": [ "startTime", "endTime" ] }, { "type": "null" } ] }, "saturday": { "anyOf": [ { "type": "object", "properties": { "startTime": { "type": "string" }, "endTime": { "type": "string" } }, "required": [ "startTime", "endTime" ] }, { "type": "null" } ] }, "sunday": { "anyOf": [ { "type": "object", "properties": { "startTime": { "type": "string" }, "endTime": { "type": "string" } }, "required": [ "startTime", "endTime" ] }, { "type": "null" } ] } }, "required": [ "siteId", "monday", "tuesday", "wednesday", "thursday", "friday", "saturday", "sunday" ] }, { "type": "null" } ] }, "qualifiedSessionTypeIds": { "type": "array", "items": { "type": "string", "format": "uuid", "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$" } }, "legacyId": { "anyOf": [ { "type": "string" }, { "type": "null" } ] }, "createdAt": { "type": "string", "format": "date-time", "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$" }, "updatedAt": { "type": "string", "format": "date-time", "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$" } }, "required": [ "object", "id", "firstName", "lastName", "email", "phone", "status", "employeeNumber", "instructor", "photoUrl", "bio", "siteIds", "homeSiteId", "timezone", "workingHours", "qualifiedSessionTypeIds", "legacyId", "createdAt", "updatedAt" ] } }, "hasMore": { "type": "boolean" }, "nextCursor": { "type": [ "string", "null" ] }, "url": { "type": "string" } }, "required": [ "object", "data", "hasMore", "url" ] } } } } ``` ### 400 — Malformed request ```json { "description": "Malformed request", "content": { "application/json": { "schema": { "type": "object", "properties": { "error": { "type": "string" }, "code": { "type": "string" }, "message": { "type": "string" } }, "required": [ "error" ] } } } } ``` ### 403 — The API key does not have the required scope ```json { "description": "The API key does not have the required scope", "content": { "application/json": { "schema": { "type": "object", "properties": { "error": { "type": "string" }, "code": { "type": "string" }, "message": { "type": "string" } }, "required": [ "error" ] } } } } ``` ### 404 — Not found. A record that belongs to another brand also returns 404. ```json { "description": "Not found. A record that belongs to another brand also returns 404.", "content": { "application/json": { "schema": { "type": "object", "properties": { "error": { "type": "string" }, "code": { "type": "string" }, "message": { "type": "string" } }, "required": [ "error" ] } } } } ``` ## Schemas in scope Refer to the full OpenAPI spec for resolved `$ref` definitions: - https://docs.hapana-developers.com/openapi/business-v2.json --- Source spec: `docs/openapi/hapana-business-api.openapi.json` · operationId `get_staff` · version `v2` ## Security schemes ```json { "partnerKey": { "type": "http", "scheme": "bearer", "description": "The brand API key, e.g. `hk_test_…`. Paste the value only, without `Bearer`." } } ``` # POST /public-api/v2/staff **Hapana Business API v2** **Base URL**: `https://public-api.hapana-app.com/public-api/v2` ## Summary Create a staff member ## Description staffAccessPolicyId comes from GET /staff-access-policies and has no default: the policy decides what the new staff member can do in Hapana. **Required scope:** `staff:write` **Tags**: Staff **Authentication**: ```json [ { "partnerKey": [] } ] ``` ## Parameters | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | Idempotency-Key | header | string | no | Makes the request safe to retry. A retry with the same key returns the first response; reusing a key with a different body is refused with 422. | ## Request Body ```json { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "firstName": { "type": "string", "minLength": 1, "maxLength": 255 }, "lastName": { "type": "string", "minLength": 1, "maxLength": 255 }, "email": { "type": "string", "format": "email", "pattern": "^(?!\\.)(?!.*\\.\\.)([A-Za-z0-9_'+\\-\\.]*)[A-Za-z0-9_+-]@([A-Za-z0-9][A-Za-z0-9\\-]*\\.)+[A-Za-z]{2,}$" }, "phone": { "type": "string", "maxLength": 64 }, "staffAccessPolicyId": { "type": "string", "format": "uuid", "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$" }, "siteIds": { "minItems": 1, "type": "array", "items": { "type": "string", "format": "uuid", "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$" } } }, "required": [ "firstName", "lastName", "email", "phone", "staffAccessPolicyId", "siteIds" ] } } } } ``` ## Responses ### 201 — Success ```json { "description": "Success", "content": { "application/json": { "schema": { "type": "object", "properties": { "object": { "type": "string", "const": "staff" }, "id": { "type": "string", "format": "uuid", "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$" }, "firstName": { "type": "string" }, "lastName": { "type": "string" }, "email": { "anyOf": [ { "type": "string" }, { "type": "null" } ] }, "phone": { "anyOf": [ { "type": "string" }, { "type": "null" } ] }, "status": { "type": "string", "enum": [ "active", "inactive" ] }, "employeeNumber": { "anyOf": [ { "type": "string" }, { "type": "null" } ] }, "instructor": { "type": "boolean" }, "photoUrl": { "anyOf": [ { "type": "string" }, { "type": "null" } ] }, "bio": { "anyOf": [ { "type": "string" }, { "type": "null" } ] }, "siteIds": { "type": "array", "items": { "type": "string", "format": "uuid", "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$" } }, "homeSiteId": { "anyOf": [ { "type": "string", "format": "uuid", "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$" }, { "type": "null" } ] }, "timezone": { "anyOf": [ { "type": "string" }, { "type": "null" } ] }, "workingHours": { "anyOf": [ { "type": "object", "properties": { "siteId": { "type": "string", "format": "uuid", "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$" }, "monday": { "anyOf": [ { "type": "object", "properties": { "startTime": { "type": "string" }, "endTime": { "type": "string" } }, "required": [ "startTime", "endTime" ] }, { "type": "null" } ] }, "tuesday": { "anyOf": [ { "type": "object", "properties": { "startTime": { "type": "string" }, "endTime": { "type": "string" } }, "required": [ "startTime", "endTime" ] }, { "type": "null" } ] }, "wednesday": { "anyOf": [ { "type": "object", "properties": { "startTime": { "type": "string" }, "endTime": { "type": "string" } }, "required": [ "startTime", "endTime" ] }, { "type": "null" } ] }, "thursday": { "anyOf": [ { "type": "object", "properties": { "startTime": { "type": "string" }, "endTime": { "type": "string" } }, "required": [ "startTime", "endTime" ] }, { "type": "null" } ] }, "friday": { "anyOf": [ { "type": "object", "properties": { "startTime": { "type": "string" }, "endTime": { "type": "string" } }, "required": [ "startTime", "endTime" ] }, { "type": "null" } ] }, "saturday": { "anyOf": [ { "type": "object", "properties": { "startTime": { "type": "string" }, "endTime": { "type": "string" } }, "required": [ "startTime", "endTime" ] }, { "type": "null" } ] }, "sunday": { "anyOf": [ { "type": "object", "properties": { "startTime": { "type": "string" }, "endTime": { "type": "string" } }, "required": [ "startTime", "endTime" ] }, { "type": "null" } ] } }, "required": [ "siteId", "monday", "tuesday", "wednesday", "thursday", "friday", "saturday", "sunday" ] }, { "type": "null" } ] }, "qualifiedSessionTypeIds": { "type": "array", "items": { "type": "string", "format": "uuid", "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$" } }, "legacyId": { "anyOf": [ { "type": "string" }, { "type": "null" } ] }, "createdAt": { "type": "string", "format": "date-time", "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$" }, "updatedAt": { "type": "string", "format": "date-time", "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$" } }, "required": [ "object", "id", "firstName", "lastName", "email", "phone", "status", "employeeNumber", "instructor", "photoUrl", "bio", "siteIds", "homeSiteId", "timezone", "workingHours", "qualifiedSessionTypeIds", "legacyId", "createdAt", "updatedAt" ] } } } } ``` ### 400 — Malformed request ```json { "description": "Malformed request", "content": { "application/json": { "schema": { "type": "object", "properties": { "error": { "type": "string" }, "code": { "type": "string" }, "message": { "type": "string" } }, "required": [ "error" ] } } } } ``` ### 403 — The API key does not have the required scope ```json { "description": "The API key does not have the required scope", "content": { "application/json": { "schema": { "type": "object", "properties": { "error": { "type": "string" }, "code": { "type": "string" }, "message": { "type": "string" } }, "required": [ "error" ] } } } } ``` ### 404 — Not found. A record that belongs to another brand also returns 404. ```json { "description": "Not found. A record that belongs to another brand also returns 404.", "content": { "application/json": { "schema": { "type": "object", "properties": { "error": { "type": "string" }, "code": { "type": "string" }, "message": { "type": "string" } }, "required": [ "error" ] } } } } ``` ### 409 — A request with this Idempotency-Key is still being processed ```json { "description": "A request with this Idempotency-Key is still being processed" } ``` ### 422 — Idempotency-Key reused with a different request body ```json { "description": "Idempotency-Key reused with a different request body" } ``` ## Schemas in scope Refer to the full OpenAPI spec for resolved `$ref` definitions: - https://docs.hapana-developers.com/openapi/business-v2.json --- Source spec: `docs/openapi/hapana-business-api.openapi.json` · operationId `post_staff` · version `v2` ## Security schemes ```json { "partnerKey": { "type": "http", "scheme": "bearer", "description": "The brand API key, e.g. `hk_test_…`. Paste the value only, without `Bearer`." } } ``` # GET /public-api/v2/staff/{id} **Hapana Business API v2** **Base URL**: `https://public-api.hapana-app.com/public-api/v2` ## Summary Retrieve a staff member ## Description **Required scope:** `staff:read` **Tags**: Staff **Authentication**: ```json [ { "partnerKey": [] } ] ``` ## Parameters | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | id | path | string | yes | | ## Responses ### 200 — Success ```json { "description": "Success", "content": { "application/json": { "schema": { "type": "object", "properties": { "object": { "type": "string", "const": "staff" }, "id": { "type": "string", "format": "uuid", "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$" }, "firstName": { "type": "string" }, "lastName": { "type": "string" }, "email": { "anyOf": [ { "type": "string" }, { "type": "null" } ] }, "phone": { "anyOf": [ { "type": "string" }, { "type": "null" } ] }, "status": { "type": "string", "enum": [ "active", "inactive" ] }, "employeeNumber": { "anyOf": [ { "type": "string" }, { "type": "null" } ] }, "instructor": { "type": "boolean" }, "photoUrl": { "anyOf": [ { "type": "string" }, { "type": "null" } ] }, "bio": { "anyOf": [ { "type": "string" }, { "type": "null" } ] }, "siteIds": { "type": "array", "items": { "type": "string", "format": "uuid", "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$" } }, "homeSiteId": { "anyOf": [ { "type": "string", "format": "uuid", "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$" }, { "type": "null" } ] }, "timezone": { "anyOf": [ { "type": "string" }, { "type": "null" } ] }, "workingHours": { "anyOf": [ { "type": "object", "properties": { "siteId": { "type": "string", "format": "uuid", "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$" }, "monday": { "anyOf": [ { "type": "object", "properties": { "startTime": { "type": "string" }, "endTime": { "type": "string" } }, "required": [ "startTime", "endTime" ] }, { "type": "null" } ] }, "tuesday": { "anyOf": [ { "type": "object", "properties": { "startTime": { "type": "string" }, "endTime": { "type": "string" } }, "required": [ "startTime", "endTime" ] }, { "type": "null" } ] }, "wednesday": { "anyOf": [ { "type": "object", "properties": { "startTime": { "type": "string" }, "endTime": { "type": "string" } }, "required": [ "startTime", "endTime" ] }, { "type": "null" } ] }, "thursday": { "anyOf": [ { "type": "object", "properties": { "startTime": { "type": "string" }, "endTime": { "type": "string" } }, "required": [ "startTime", "endTime" ] }, { "type": "null" } ] }, "friday": { "anyOf": [ { "type": "object", "properties": { "startTime": { "type": "string" }, "endTime": { "type": "string" } }, "required": [ "startTime", "endTime" ] }, { "type": "null" } ] }, "saturday": { "anyOf": [ { "type": "object", "properties": { "startTime": { "type": "string" }, "endTime": { "type": "string" } }, "required": [ "startTime", "endTime" ] }, { "type": "null" } ] }, "sunday": { "anyOf": [ { "type": "object", "properties": { "startTime": { "type": "string" }, "endTime": { "type": "string" } }, "required": [ "startTime", "endTime" ] }, { "type": "null" } ] } }, "required": [ "siteId", "monday", "tuesday", "wednesday", "thursday", "friday", "saturday", "sunday" ] }, { "type": "null" } ] }, "qualifiedSessionTypeIds": { "type": "array", "items": { "type": "string", "format": "uuid", "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$" } }, "legacyId": { "anyOf": [ { "type": "string" }, { "type": "null" } ] }, "createdAt": { "type": "string", "format": "date-time", "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$" }, "updatedAt": { "type": "string", "format": "date-time", "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$" } }, "required": [ "object", "id", "firstName", "lastName", "email", "phone", "status", "employeeNumber", "instructor", "photoUrl", "bio", "siteIds", "homeSiteId", "timezone", "workingHours", "qualifiedSessionTypeIds", "legacyId", "createdAt", "updatedAt" ] } } } } ``` ### 400 — Malformed request ```json { "description": "Malformed request", "content": { "application/json": { "schema": { "type": "object", "properties": { "error": { "type": "string" }, "code": { "type": "string" }, "message": { "type": "string" } }, "required": [ "error" ] } } } } ``` ### 403 — The API key does not have the required scope ```json { "description": "The API key does not have the required scope", "content": { "application/json": { "schema": { "type": "object", "properties": { "error": { "type": "string" }, "code": { "type": "string" }, "message": { "type": "string" } }, "required": [ "error" ] } } } } ``` ### 404 — Not found. A record that belongs to another brand also returns 404. ```json { "description": "Not found. A record that belongs to another brand also returns 404.", "content": { "application/json": { "schema": { "type": "object", "properties": { "error": { "type": "string" }, "code": { "type": "string" }, "message": { "type": "string" } }, "required": [ "error" ] } } } } ``` ## Schemas in scope Refer to the full OpenAPI spec for resolved `$ref` definitions: - https://docs.hapana-developers.com/openapi/business-v2.json --- Source spec: `docs/openapi/hapana-business-api.openapi.json` · operationId `get_staff__id_` · version `v2` ## Security schemes ```json { "partnerKey": { "type": "http", "scheme": "bearer", "description": "The brand API key, e.g. `hk_test_…`. Paste the value only, without `Bearer`." } } ``` # PATCH /public-api/v2/staff/{id} **Hapana Business API v2** **Base URL**: `https://public-api.hapana-app.com/public-api/v2` ## Summary Update a staff member ## Description **Required scope:** `staff:write` **Tags**: Staff **Authentication**: ```json [ { "partnerKey": [] } ] ``` ## Parameters | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | id | path | string | yes | | | Idempotency-Key | header | string | no | Makes the request safe to retry. A retry with the same key returns the first response; reusing a key with a different body is refused with 422. | ## Request Body ```json { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "firstName": { "type": "string", "minLength": 1, "maxLength": 255 }, "lastName": { "type": "string", "minLength": 1, "maxLength": 255 }, "email": { "type": "string", "format": "email", "pattern": "^(?!\\.)(?!.*\\.\\.)([A-Za-z0-9_'+\\-\\.]*)[A-Za-z0-9_+-]@([A-Za-z0-9][A-Za-z0-9\\-]*\\.)+[A-Za-z]{2,}$" }, "phone": { "type": "string", "maxLength": 64 } } } } } } ``` ## Responses ### 200 — Success ```json { "description": "Success", "content": { "application/json": { "schema": { "type": "object", "properties": { "object": { "type": "string", "const": "staff" }, "id": { "type": "string", "format": "uuid", "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$" }, "firstName": { "type": "string" }, "lastName": { "type": "string" }, "email": { "anyOf": [ { "type": "string" }, { "type": "null" } ] }, "phone": { "anyOf": [ { "type": "string" }, { "type": "null" } ] }, "status": { "type": "string", "enum": [ "active", "inactive" ] }, "employeeNumber": { "anyOf": [ { "type": "string" }, { "type": "null" } ] }, "instructor": { "type": "boolean" }, "photoUrl": { "anyOf": [ { "type": "string" }, { "type": "null" } ] }, "bio": { "anyOf": [ { "type": "string" }, { "type": "null" } ] }, "siteIds": { "type": "array", "items": { "type": "string", "format": "uuid", "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$" } }, "homeSiteId": { "anyOf": [ { "type": "string", "format": "uuid", "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$" }, { "type": "null" } ] }, "timezone": { "anyOf": [ { "type": "string" }, { "type": "null" } ] }, "workingHours": { "anyOf": [ { "type": "object", "properties": { "siteId": { "type": "string", "format": "uuid", "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$" }, "monday": { "anyOf": [ { "type": "object", "properties": { "startTime": { "type": "string" }, "endTime": { "type": "string" } }, "required": [ "startTime", "endTime" ] }, { "type": "null" } ] }, "tuesday": { "anyOf": [ { "type": "object", "properties": { "startTime": { "type": "string" }, "endTime": { "type": "string" } }, "required": [ "startTime", "endTime" ] }, { "type": "null" } ] }, "wednesday": { "anyOf": [ { "type": "object", "properties": { "startTime": { "type": "string" }, "endTime": { "type": "string" } }, "required": [ "startTime", "endTime" ] }, { "type": "null" } ] }, "thursday": { "anyOf": [ { "type": "object", "properties": { "startTime": { "type": "string" }, "endTime": { "type": "string" } }, "required": [ "startTime", "endTime" ] }, { "type": "null" } ] }, "friday": { "anyOf": [ { "type": "object", "properties": { "startTime": { "type": "string" }, "endTime": { "type": "string" } }, "required": [ "startTime", "endTime" ] }, { "type": "null" } ] }, "saturday": { "anyOf": [ { "type": "object", "properties": { "startTime": { "type": "string" }, "endTime": { "type": "string" } }, "required": [ "startTime", "endTime" ] }, { "type": "null" } ] }, "sunday": { "anyOf": [ { "type": "object", "properties": { "startTime": { "type": "string" }, "endTime": { "type": "string" } }, "required": [ "startTime", "endTime" ] }, { "type": "null" } ] } }, "required": [ "siteId", "monday", "tuesday", "wednesday", "thursday", "friday", "saturday", "sunday" ] }, { "type": "null" } ] }, "qualifiedSessionTypeIds": { "type": "array", "items": { "type": "string", "format": "uuid", "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$" } }, "legacyId": { "anyOf": [ { "type": "string" }, { "type": "null" } ] }, "createdAt": { "type": "string", "format": "date-time", "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$" }, "updatedAt": { "type": "string", "format": "date-time", "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$" } }, "required": [ "object", "id", "firstName", "lastName", "email", "phone", "status", "employeeNumber", "instructor", "photoUrl", "bio", "siteIds", "homeSiteId", "timezone", "workingHours", "qualifiedSessionTypeIds", "legacyId", "createdAt", "updatedAt" ] } } } } ``` ### 400 — Malformed request ```json { "description": "Malformed request", "content": { "application/json": { "schema": { "type": "object", "properties": { "error": { "type": "string" }, "code": { "type": "string" }, "message": { "type": "string" } }, "required": [ "error" ] } } } } ``` ### 403 — The API key does not have the required scope ```json { "description": "The API key does not have the required scope", "content": { "application/json": { "schema": { "type": "object", "properties": { "error": { "type": "string" }, "code": { "type": "string" }, "message": { "type": "string" } }, "required": [ "error" ] } } } } ``` ### 404 — Not found. A record that belongs to another brand also returns 404. ```json { "description": "Not found. A record that belongs to another brand also returns 404.", "content": { "application/json": { "schema": { "type": "object", "properties": { "error": { "type": "string" }, "code": { "type": "string" }, "message": { "type": "string" } }, "required": [ "error" ] } } } } ``` ### 409 — A request with this Idempotency-Key is still being processed ```json { "description": "A request with this Idempotency-Key is still being processed" } ``` ### 422 — Idempotency-Key reused with a different request body ```json { "description": "Idempotency-Key reused with a different request body" } ``` ## Schemas in scope Refer to the full OpenAPI spec for resolved `$ref` definitions: - https://docs.hapana-developers.com/openapi/business-v2.json --- Source spec: `docs/openapi/hapana-business-api.openapi.json` · operationId `patch_staff__id_` · version `v2` ## Security schemes ```json { "partnerKey": { "type": "http", "scheme": "bearer", "description": "The brand API key, e.g. `hk_test_…`. Paste the value only, without `Bearer`." } } ``` # DELETE /public-api/v2/staff/{id} **Hapana Business API v2** **Base URL**: `https://public-api.hapana-app.com/public-api/v2` ## Summary Deactivate a staff member ## Description Deactivates rather than deletes, so the person’s bookings, check-ins and history are kept. **Required scope:** `staff:write` **Tags**: Staff **Authentication**: ```json [ { "partnerKey": [] } ] ``` ## Parameters | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | id | path | string | yes | | | Idempotency-Key | header | string | no | Makes the request safe to retry. A retry with the same key returns the first response; reusing a key with a different body is refused with 422. | ## Responses ### 204 — No content ```json { "description": "No content" } ``` ### 400 — Malformed request ```json { "description": "Malformed request", "content": { "application/json": { "schema": { "type": "object", "properties": { "error": { "type": "string" }, "code": { "type": "string" }, "message": { "type": "string" } }, "required": [ "error" ] } } } } ``` ### 403 — The API key does not have the required scope ```json { "description": "The API key does not have the required scope", "content": { "application/json": { "schema": { "type": "object", "properties": { "error": { "type": "string" }, "code": { "type": "string" }, "message": { "type": "string" } }, "required": [ "error" ] } } } } ``` ### 404 — Not found. A record that belongs to another brand also returns 404. ```json { "description": "Not found. A record that belongs to another brand also returns 404.", "content": { "application/json": { "schema": { "type": "object", "properties": { "error": { "type": "string" }, "code": { "type": "string" }, "message": { "type": "string" } }, "required": [ "error" ] } } } } ``` ### 409 — A request with this Idempotency-Key is still being processed ```json { "description": "A request with this Idempotency-Key is still being processed" } ``` ### 422 — Idempotency-Key reused with a different request body ```json { "description": "Idempotency-Key reused with a different request body" } ``` ## Schemas in scope Refer to the full OpenAPI spec for resolved `$ref` definitions: - https://docs.hapana-developers.com/openapi/business-v2.json --- Source spec: `docs/openapi/hapana-business-api.openapi.json` · operationId `delete_staff__id_` · version `v2` ## Security schemes ```json { "partnerKey": { "type": "http", "scheme": "bearer", "description": "The brand API key, e.g. `hk_test_…`. Paste the value only, without `Bearer`." } } ``` # GET /public-api/v2/sessions **Hapana Business API v2** **Base URL**: `https://public-api.hapana-app.com/public-api/v2` ## Summary List sessions in a date range ## Description siteId, startDate and endDate are required. Each session includes the instructors assigned to it. **Required scope:** `sessions:read` **Tags**: Sessions **Authentication**: ```json [ { "partnerKey": [] } ] ``` ## Parameters | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | cursor | query | string | no | | | limit | query | integer | no | | | siteId | query | | no | | | startDate | query | string | yes | | | endDate | query | string | yes | | | status | query | string | no | | | sessionTypeId | query | string | no | | | updatedSince | query | string | no | | ## Responses ### 200 — Success ```json { "description": "Success", "content": { "application/json": { "schema": { "type": "object", "properties": { "object": { "type": "string", "enum": [ "list" ] }, "data": { "type": "array", "items": { "type": "object", "properties": { "object": { "type": "string", "const": "session" }, "id": { "type": "string" }, "siteId": { "type": "string", "format": "uuid", "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$" }, "name": { "type": "string" }, "date": { "type": "string", "format": "date", "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$" }, "startTime": { "type": "string", "format": "date-time", "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$" }, "endTime": { "type": "string", "format": "date-time", "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$" }, "status": { "type": "string", "enum": [ "SCHEDULED", "IN_PROGRESS", "COMPLETED", "CANCELLED" ] }, "cancelled": { "type": "boolean" }, "capacity": { "anyOf": [ { "type": "number" }, { "type": "null" } ] }, "bookedCount": { "anyOf": [ { "type": "number" }, { "type": "null" } ] }, "spacesRemaining": { "anyOf": [ { "type": "integer", "minimum": -9007199254740991, "maximum": 9007199254740991 }, { "type": "null" } ] }, "waitlistEnabled": { "type": "boolean" }, "waitlistAvailable": { "type": "boolean" }, "durationMinutes": { "type": "integer", "minimum": -9007199254740991, "maximum": 9007199254740991 }, "sessionTypeId": { "type": "string" }, "scheduleTemplateId": { "anyOf": [ { "type": "string" }, { "type": "null" } ] }, "timezone": { "anyOf": [ { "type": "string" }, { "type": "null" } ] }, "localStartTime": { "anyOf": [ { "type": "string" }, { "type": "null" } ] }, "localEndTime": { "anyOf": [ { "type": "string" }, { "type": "null" } ] }, "locationName": { "anyOf": [ { "type": "string" }, { "type": "null" } ] }, "roomName": { "anyOf": [ { "type": "string" }, { "type": "null" } ] }, "sessionTypeName": { "anyOf": [ { "type": "string" }, { "type": "null" } ] }, "legacyId": { "anyOf": [ { "type": "string" }, { "type": "null" } ] }, "instructors": { "type": "array", "items": { "type": "object", "properties": { "id": { "type": "string" }, "name": { "type": "string" }, "role": { "type": "string" }, "substitutingFor": { "anyOf": [ { "type": "string" }, { "type": "null" } ] } }, "required": [ "id", "name", "role", "substitutingFor" ] } } }, "required": [ "object", "id", "siteId", "name", "date", "startTime", "endTime", "status", "cancelled", "capacity", "bookedCount", "spacesRemaining", "waitlistEnabled", "waitlistAvailable", "durationMinutes", "sessionTypeId", "scheduleTemplateId", "timezone", "localStartTime", "localEndTime", "locationName", "roomName", "sessionTypeName", "legacyId", "instructors" ] } }, "hasMore": { "type": "boolean" }, "nextCursor": { "type": [ "string", "null" ] }, "url": { "type": "string" } }, "required": [ "object", "data", "hasMore", "url" ] } } } } ``` ### 400 — Malformed request ```json { "description": "Malformed request", "content": { "application/json": { "schema": { "type": "object", "properties": { "error": { "type": "string" }, "code": { "type": "string" }, "message": { "type": "string" } }, "required": [ "error" ] } } } } ``` ### 403 — The API key does not have the required scope ```json { "description": "The API key does not have the required scope", "content": { "application/json": { "schema": { "type": "object", "properties": { "error": { "type": "string" }, "code": { "type": "string" }, "message": { "type": "string" } }, "required": [ "error" ] } } } } ``` ### 404 — Not found. A record that belongs to another brand also returns 404. ```json { "description": "Not found. A record that belongs to another brand also returns 404.", "content": { "application/json": { "schema": { "type": "object", "properties": { "error": { "type": "string" }, "code": { "type": "string" }, "message": { "type": "string" } }, "required": [ "error" ] } } } } ``` ## Schemas in scope Refer to the full OpenAPI spec for resolved `$ref` definitions: - https://docs.hapana-developers.com/openapi/business-v2.json --- Source spec: `docs/openapi/hapana-business-api.openapi.json` · operationId `get_sessions` · version `v2` ## Security schemes ```json { "partnerKey": { "type": "http", "scheme": "bearer", "description": "The brand API key, e.g. `hk_test_…`. Paste the value only, without `Bearer`." } } ``` # POST /public-api/v2/sessions **Hapana Business API v2** **Base URL**: `https://public-api.hapana-app.com/public-api/v2` ## Summary Create a session ## Description Schedules a single class, not a recurring series. The request is refused if the class falls outside the room’s opening hours or the instructor’s working hours, or clashes with another class. **Required scope:** `sessions:write` **Tags**: Sessions **Authentication**: ```json [ { "partnerKey": [] } ] ``` ## Parameters | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | Idempotency-Key | header | string | no | Makes the request safe to retry. A retry with the same key returns the first response; reusing a key with a different body is refused with 422. | ## Request Body ```json { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "siteId": { "type": "string", "format": "uuid", "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$" }, "name": { "type": "string", "minLength": 1, "maxLength": 255 }, "category": { "type": "string", "enum": [ "CLASS", "PRIVATE", "WORKSHOP" ] }, "date": { "type": "string", "format": "date", "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$" }, "startTime": { "type": "string", "format": "date-time", "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$" }, "endTime": { "type": "string", "format": "date-time", "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$" }, "siteSessionTypeId": { "type": "string", "format": "uuid", "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$" }, "siteLocationId": { "type": "string", "format": "uuid", "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$" }, "dropinPrice": { "type": "number", "minimum": 0 }, "capacity": { "type": "integer", "minimum": 0, "maximum": 9007199254740991 } }, "required": [ "siteId", "name", "category", "date", "startTime", "endTime", "siteSessionTypeId", "siteLocationId", "dropinPrice" ] } } } } ``` ## Responses ### 201 — Success ```json { "description": "Success", "content": { "application/json": { "schema": { "type": "object", "properties": { "object": { "type": "string", "const": "session" }, "id": { "type": "string" }, "siteId": { "type": "string", "format": "uuid", "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$" }, "name": { "type": "string" }, "date": { "type": "string", "format": "date", "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$" }, "startTime": { "type": "string", "format": "date-time", "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$" }, "endTime": { "type": "string", "format": "date-time", "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$" }, "status": { "type": "string", "enum": [ "SCHEDULED", "IN_PROGRESS", "COMPLETED", "CANCELLED" ] }, "cancelled": { "type": "boolean" }, "capacity": { "anyOf": [ { "type": "number" }, { "type": "null" } ] }, "bookedCount": { "anyOf": [ { "type": "number" }, { "type": "null" } ] }, "spacesRemaining": { "anyOf": [ { "type": "integer", "minimum": -9007199254740991, "maximum": 9007199254740991 }, { "type": "null" } ] }, "waitlistEnabled": { "type": "boolean" }, "waitlistAvailable": { "type": "boolean" }, "durationMinutes": { "type": "integer", "minimum": -9007199254740991, "maximum": 9007199254740991 }, "sessionTypeId": { "type": "string" }, "scheduleTemplateId": { "anyOf": [ { "type": "string" }, { "type": "null" } ] }, "timezone": { "anyOf": [ { "type": "string" }, { "type": "null" } ] }, "localStartTime": { "anyOf": [ { "type": "string" }, { "type": "null" } ] }, "localEndTime": { "anyOf": [ { "type": "string" }, { "type": "null" } ] }, "locationName": { "anyOf": [ { "type": "string" }, { "type": "null" } ] }, "roomName": { "anyOf": [ { "type": "string" }, { "type": "null" } ] }, "sessionTypeName": { "anyOf": [ { "type": "string" }, { "type": "null" } ] }, "legacyId": { "anyOf": [ { "type": "string" }, { "type": "null" } ] }, "instructors": { "type": "array", "items": { "type": "object", "properties": { "id": { "type": "string" }, "name": { "type": "string" }, "role": { "type": "string" }, "substitutingFor": { "anyOf": [ { "type": "string" }, { "type": "null" } ] } }, "required": [ "id", "name", "role", "substitutingFor" ] } } }, "required": [ "object", "id", "siteId", "name", "date", "startTime", "endTime", "status", "cancelled", "capacity", "bookedCount", "spacesRemaining", "waitlistEnabled", "waitlistAvailable", "durationMinutes", "sessionTypeId", "scheduleTemplateId", "timezone", "localStartTime", "localEndTime", "locationName", "roomName", "sessionTypeName", "legacyId", "instructors" ] } } } } ``` ### 400 — Malformed request ```json { "description": "Malformed request", "content": { "application/json": { "schema": { "type": "object", "properties": { "error": { "type": "string" }, "code": { "type": "string" }, "message": { "type": "string" } }, "required": [ "error" ] } } } } ``` ### 403 — The API key does not have the required scope ```json { "description": "The API key does not have the required scope", "content": { "application/json": { "schema": { "type": "object", "properties": { "error": { "type": "string" }, "code": { "type": "string" }, "message": { "type": "string" } }, "required": [ "error" ] } } } } ``` ### 404 — Not found. A record that belongs to another brand also returns 404. ```json { "description": "Not found. A record that belongs to another brand also returns 404.", "content": { "application/json": { "schema": { "type": "object", "properties": { "error": { "type": "string" }, "code": { "type": "string" }, "message": { "type": "string" } }, "required": [ "error" ] } } } } ``` ### 409 — A request with this Idempotency-Key is still being processed ```json { "description": "A request with this Idempotency-Key is still being processed" } ``` ### 422 — Idempotency-Key reused with a different request body ```json { "description": "Idempotency-Key reused with a different request body" } ``` ## Schemas in scope Refer to the full OpenAPI spec for resolved `$ref` definitions: - https://docs.hapana-developers.com/openapi/business-v2.json --- Source spec: `docs/openapi/hapana-business-api.openapi.json` · operationId `post_sessions` · version `v2` ## Security schemes ```json { "partnerKey": { "type": "http", "scheme": "bearer", "description": "The brand API key, e.g. `hk_test_…`. Paste the value only, without `Bearer`." } } ``` # GET /public-api/v2/sessions/types **Hapana Business API v2** **Base URL**: `https://public-api.hapana-app.com/public-api/v2` ## Summary List session types ## Description The class types you can create a session as. **Required scope:** `sessions:read` **Tags**: Sessions **Authentication**: ```json [ { "partnerKey": [] } ] ``` ## Parameters | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | siteId | query | string | yes | | ## Responses ### 200 — Success ```json { "description": "Success", "content": { "application/json": { "schema": { "type": "object", "properties": { "object": { "type": "string", "enum": [ "list" ] }, "data": { "type": "array", "items": { "type": "object", "properties": { "object": { "type": "string", "const": "session_type" }, "id": { "type": "string", "format": "uuid", "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$" }, "siteId": { "type": "string", "format": "uuid", "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$" }, "name": { "type": "string" }, "category": { "anyOf": [ { "type": "string" }, { "type": "null" } ] } }, "required": [ "object", "id", "siteId", "name", "category" ] } }, "hasMore": { "type": "boolean" }, "nextCursor": { "type": [ "string", "null" ] }, "url": { "type": "string" } }, "required": [ "object", "data", "hasMore", "url" ] } } } } ``` ### 400 — Malformed request ```json { "description": "Malformed request", "content": { "application/json": { "schema": { "type": "object", "properties": { "error": { "type": "string" }, "code": { "type": "string" }, "message": { "type": "string" } }, "required": [ "error" ] } } } } ``` ### 403 — The API key does not have the required scope ```json { "description": "The API key does not have the required scope", "content": { "application/json": { "schema": { "type": "object", "properties": { "error": { "type": "string" }, "code": { "type": "string" }, "message": { "type": "string" } }, "required": [ "error" ] } } } } ``` ### 404 — Not found. A record that belongs to another brand also returns 404. ```json { "description": "Not found. A record that belongs to another brand also returns 404.", "content": { "application/json": { "schema": { "type": "object", "properties": { "error": { "type": "string" }, "code": { "type": "string" }, "message": { "type": "string" } }, "required": [ "error" ] } } } } ``` ## Schemas in scope Refer to the full OpenAPI spec for resolved `$ref` definitions: - https://docs.hapana-developers.com/openapi/business-v2.json --- Source spec: `docs/openapi/hapana-business-api.openapi.json` · operationId `get_sessions_types` · version `v2` ## Security schemes ```json { "partnerKey": { "type": "http", "scheme": "bearer", "description": "The brand API key, e.g. `hk_test_…`. Paste the value only, without `Bearer`." } } ``` # GET /public-api/v2/sessions/templates **Hapana Business API v2** **Base URL**: `https://public-api.hapana-app.com/public-api/v2` ## Summary List schedule templates ## Description The templates a site generates its classes from: the session type, the staff eligible to teach it (instructorIds) and the recurring hours it runs at (schedules). A session joins back here through scheduleTemplateId. An empty instructorIds means any instructor at the site may teach it. siteId is required. **Required scope:** `sessions:read` **Tags**: Sessions **Authentication**: ```json [ { "partnerKey": [] } ] ``` ## Parameters | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | cursor | query | string | no | | | limit | query | integer | no | | | siteId | query | string | yes | | ## Responses ### 200 — Success ```json { "description": "Success", "content": { "application/json": { "schema": { "type": "object", "properties": { "object": { "type": "string", "enum": [ "list" ] }, "data": { "type": "array", "items": { "type": "object", "properties": { "object": { "type": "string", "const": "session_template" }, "id": { "type": "string" }, "siteId": { "type": "string", "format": "uuid", "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$" }, "name": { "type": "string" }, "description": { "anyOf": [ { "type": "string" }, { "type": "null" } ] }, "category": { "type": "string", "enum": [ "CLASS", "PRIVATE", "WORKSHOP" ] }, "sessionTypeId": { "type": "string" }, "sessionTypeName": { "anyOf": [ { "type": "string" }, { "type": "null" } ] }, "durationMinutes": { "type": "integer", "minimum": -9007199254740991, "maximum": 9007199254740991 }, "capacity": { "type": "integer", "minimum": -9007199254740991, "maximum": 9007199254740991 }, "locationId": { "type": "string" }, "roomId": { "anyOf": [ { "type": "string" }, { "type": "null" } ] }, "instructorIds": { "description": "The staff marked as able to teach this template. Empty means unrestricted: any instructor at the site may teach it.", "type": "array", "items": { "type": "string" } }, "schedules": { "type": "array", "items": { "type": "object", "properties": { "id": { "type": "string" }, "recurrenceType": { "type": "string", "enum": [ "DAILY", "WEEKLY", "MONTHLY" ] }, "interval": { "type": "integer", "minimum": -9007199254740991, "maximum": 9007199254740991 }, "daysOfWeek": { "anyOf": [ { "type": "array", "items": { "type": "string", "enum": [ "MONDAY", "TUESDAY", "WEDNESDAY", "THURSDAY", "FRIDAY", "SATURDAY", "SUNDAY" ] } }, { "type": "null" } ] }, "dayOfMonth": { "anyOf": [ { "type": "integer", "minimum": -9007199254740991, "maximum": 9007199254740991 }, { "type": "null" } ] }, "startTime": { "type": "string" }, "endTime": { "type": "string" }, "startDate": { "type": "string", "format": "date", "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$" }, "endDate": { "anyOf": [ { "type": "string", "format": "date", "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$" }, { "type": "null" } ] }, "endAfterOccurrences": { "description": "Set when the series stops after this many classes rather than on a date. Null with a null endDate is an open-ended series.", "anyOf": [ { "type": "integer", "minimum": -9007199254740991, "maximum": 9007199254740991 }, { "type": "null" } ] }, "instructorIds": { "type": "array", "items": { "type": "string" } } }, "required": [ "id", "recurrenceType", "interval", "daysOfWeek", "dayOfMonth", "startTime", "endTime", "startDate", "endDate", "endAfterOccurrences", "instructorIds" ] } }, "createdAt": { "type": "string", "format": "date-time", "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$" }, "updatedAt": { "type": "string", "format": "date-time", "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$" } }, "required": [ "object", "id", "siteId", "name", "description", "category", "sessionTypeId", "sessionTypeName", "durationMinutes", "capacity", "locationId", "roomId", "instructorIds", "schedules", "createdAt", "updatedAt" ] } }, "hasMore": { "type": "boolean" }, "nextCursor": { "type": [ "string", "null" ] }, "url": { "type": "string" } }, "required": [ "object", "data", "hasMore", "url" ] } } } } ``` ### 400 — Malformed request ```json { "description": "Malformed request", "content": { "application/json": { "schema": { "type": "object", "properties": { "error": { "type": "string" }, "code": { "type": "string" }, "message": { "type": "string" } }, "required": [ "error" ] } } } } ``` ### 403 — The API key does not have the required scope ```json { "description": "The API key does not have the required scope", "content": { "application/json": { "schema": { "type": "object", "properties": { "error": { "type": "string" }, "code": { "type": "string" }, "message": { "type": "string" } }, "required": [ "error" ] } } } } ``` ### 404 — Not found. A record that belongs to another brand also returns 404. ```json { "description": "Not found. A record that belongs to another brand also returns 404.", "content": { "application/json": { "schema": { "type": "object", "properties": { "error": { "type": "string" }, "code": { "type": "string" }, "message": { "type": "string" } }, "required": [ "error" ] } } } } ``` ## Schemas in scope Refer to the full OpenAPI spec for resolved `$ref` definitions: - https://docs.hapana-developers.com/openapi/business-v2.json --- Source spec: `docs/openapi/hapana-business-api.openapi.json` · operationId `get_sessions_templates` · version `v2` ## Security schemes ```json { "partnerKey": { "type": "http", "scheme": "bearer", "description": "The brand API key, e.g. `hk_test_…`. Paste the value only, without `Bearer`." } } ``` # GET /public-api/v2/sessions/templates/{id} **Hapana Business API v2** **Base URL**: `https://public-api.hapana-app.com/public-api/v2` ## Summary Retrieve a schedule template ## Description One template with its eligible instructors and recurring schedules. **Required scope:** `sessions:read` **Tags**: Sessions **Authentication**: ```json [ { "partnerKey": [] } ] ``` ## Parameters | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | id | path | string | yes | | | siteId | query | string | yes | | ## Responses ### 200 — Success ```json { "description": "Success", "content": { "application/json": { "schema": { "type": "object", "properties": { "object": { "type": "string", "const": "session_template" }, "id": { "type": "string" }, "siteId": { "type": "string", "format": "uuid", "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$" }, "name": { "type": "string" }, "description": { "anyOf": [ { "type": "string" }, { "type": "null" } ] }, "category": { "type": "string", "enum": [ "CLASS", "PRIVATE", "WORKSHOP" ] }, "sessionTypeId": { "type": "string" }, "sessionTypeName": { "anyOf": [ { "type": "string" }, { "type": "null" } ] }, "durationMinutes": { "type": "integer", "minimum": -9007199254740991, "maximum": 9007199254740991 }, "capacity": { "type": "integer", "minimum": -9007199254740991, "maximum": 9007199254740991 }, "locationId": { "type": "string" }, "roomId": { "anyOf": [ { "type": "string" }, { "type": "null" } ] }, "instructorIds": { "description": "The staff marked as able to teach this template. Empty means unrestricted: any instructor at the site may teach it.", "type": "array", "items": { "type": "string" } }, "schedules": { "type": "array", "items": { "type": "object", "properties": { "id": { "type": "string" }, "recurrenceType": { "type": "string", "enum": [ "DAILY", "WEEKLY", "MONTHLY" ] }, "interval": { "type": "integer", "minimum": -9007199254740991, "maximum": 9007199254740991 }, "daysOfWeek": { "anyOf": [ { "type": "array", "items": { "type": "string", "enum": [ "MONDAY", "TUESDAY", "WEDNESDAY", "THURSDAY", "FRIDAY", "SATURDAY", "SUNDAY" ] } }, { "type": "null" } ] }, "dayOfMonth": { "anyOf": [ { "type": "integer", "minimum": -9007199254740991, "maximum": 9007199254740991 }, { "type": "null" } ] }, "startTime": { "type": "string" }, "endTime": { "type": "string" }, "startDate": { "type": "string", "format": "date", "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$" }, "endDate": { "anyOf": [ { "type": "string", "format": "date", "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$" }, { "type": "null" } ] }, "endAfterOccurrences": { "description": "Set when the series stops after this many classes rather than on a date. Null with a null endDate is an open-ended series.", "anyOf": [ { "type": "integer", "minimum": -9007199254740991, "maximum": 9007199254740991 }, { "type": "null" } ] }, "instructorIds": { "type": "array", "items": { "type": "string" } } }, "required": [ "id", "recurrenceType", "interval", "daysOfWeek", "dayOfMonth", "startTime", "endTime", "startDate", "endDate", "endAfterOccurrences", "instructorIds" ] } }, "createdAt": { "type": "string", "format": "date-time", "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$" }, "updatedAt": { "type": "string", "format": "date-time", "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$" } }, "required": [ "object", "id", "siteId", "name", "description", "category", "sessionTypeId", "sessionTypeName", "durationMinutes", "capacity", "locationId", "roomId", "instructorIds", "schedules", "createdAt", "updatedAt" ] } } } } ``` ### 400 — Malformed request ```json { "description": "Malformed request", "content": { "application/json": { "schema": { "type": "object", "properties": { "error": { "type": "string" }, "code": { "type": "string" }, "message": { "type": "string" } }, "required": [ "error" ] } } } } ``` ### 403 — The API key does not have the required scope ```json { "description": "The API key does not have the required scope", "content": { "application/json": { "schema": { "type": "object", "properties": { "error": { "type": "string" }, "code": { "type": "string" }, "message": { "type": "string" } }, "required": [ "error" ] } } } } ``` ### 404 — Not found. A record that belongs to another brand also returns 404. ```json { "description": "Not found. A record that belongs to another brand also returns 404.", "content": { "application/json": { "schema": { "type": "object", "properties": { "error": { "type": "string" }, "code": { "type": "string" }, "message": { "type": "string" } }, "required": [ "error" ] } } } } ``` ## Schemas in scope Refer to the full OpenAPI spec for resolved `$ref` definitions: - https://docs.hapana-developers.com/openapi/business-v2.json --- Source spec: `docs/openapi/hapana-business-api.openapi.json` · operationId `get_sessions_templates__id_` · version `v2` ## Security schemes ```json { "partnerKey": { "type": "http", "scheme": "bearer", "description": "The brand API key, e.g. `hk_test_…`. Paste the value only, without `Bearer`." } } ``` # GET /public-api/v2/sessions/{id} **Hapana Business API v2** **Base URL**: `https://public-api.hapana-app.com/public-api/v2` ## Summary Retrieve a session ## Description A class in a recurring series that has not been edited individually may have an id that is not a UUID. Pass the id exactly as GET /sessions returned it. **Required scope:** `sessions:read` **Tags**: Sessions **Authentication**: ```json [ { "partnerKey": [] } ] ``` ## Parameters | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | id | path | string | yes | | | siteId | query | string | yes | | ## Responses ### 200 — Success ```json { "description": "Success", "content": { "application/json": { "schema": { "type": "object", "properties": { "object": { "type": "string", "const": "session" }, "id": { "type": "string" }, "siteId": { "type": "string", "format": "uuid", "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$" }, "name": { "type": "string" }, "date": { "type": "string", "format": "date", "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$" }, "startTime": { "type": "string", "format": "date-time", "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$" }, "endTime": { "type": "string", "format": "date-time", "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$" }, "status": { "type": "string", "enum": [ "SCHEDULED", "IN_PROGRESS", "COMPLETED", "CANCELLED" ] }, "cancelled": { "type": "boolean" }, "capacity": { "anyOf": [ { "type": "number" }, { "type": "null" } ] }, "bookedCount": { "anyOf": [ { "type": "number" }, { "type": "null" } ] }, "spacesRemaining": { "anyOf": [ { "type": "integer", "minimum": -9007199254740991, "maximum": 9007199254740991 }, { "type": "null" } ] }, "waitlistEnabled": { "type": "boolean" }, "waitlistAvailable": { "type": "boolean" }, "durationMinutes": { "type": "integer", "minimum": -9007199254740991, "maximum": 9007199254740991 }, "sessionTypeId": { "type": "string" }, "scheduleTemplateId": { "anyOf": [ { "type": "string" }, { "type": "null" } ] }, "timezone": { "anyOf": [ { "type": "string" }, { "type": "null" } ] }, "localStartTime": { "anyOf": [ { "type": "string" }, { "type": "null" } ] }, "localEndTime": { "anyOf": [ { "type": "string" }, { "type": "null" } ] }, "locationName": { "anyOf": [ { "type": "string" }, { "type": "null" } ] }, "roomName": { "anyOf": [ { "type": "string" }, { "type": "null" } ] }, "sessionTypeName": { "anyOf": [ { "type": "string" }, { "type": "null" } ] }, "legacyId": { "anyOf": [ { "type": "string" }, { "type": "null" } ] }, "instructors": { "type": "array", "items": { "type": "object", "properties": { "id": { "type": "string" }, "name": { "type": "string" }, "role": { "type": "string" }, "substitutingFor": { "anyOf": [ { "type": "string" }, { "type": "null" } ] } }, "required": [ "id", "name", "role", "substitutingFor" ] } } }, "required": [ "object", "id", "siteId", "name", "date", "startTime", "endTime", "status", "cancelled", "capacity", "bookedCount", "spacesRemaining", "waitlistEnabled", "waitlistAvailable", "durationMinutes", "sessionTypeId", "scheduleTemplateId", "timezone", "localStartTime", "localEndTime", "locationName", "roomName", "sessionTypeName", "legacyId", "instructors" ] } } } } ``` ### 400 — Malformed request ```json { "description": "Malformed request", "content": { "application/json": { "schema": { "type": "object", "properties": { "error": { "type": "string" }, "code": { "type": "string" }, "message": { "type": "string" } }, "required": [ "error" ] } } } } ``` ### 403 — The API key does not have the required scope ```json { "description": "The API key does not have the required scope", "content": { "application/json": { "schema": { "type": "object", "properties": { "error": { "type": "string" }, "code": { "type": "string" }, "message": { "type": "string" } }, "required": [ "error" ] } } } } ``` ### 404 — Not found. A record that belongs to another brand also returns 404. ```json { "description": "Not found. A record that belongs to another brand also returns 404.", "content": { "application/json": { "schema": { "type": "object", "properties": { "error": { "type": "string" }, "code": { "type": "string" }, "message": { "type": "string" } }, "required": [ "error" ] } } } } ``` ## Schemas in scope Refer to the full OpenAPI spec for resolved `$ref` definitions: - https://docs.hapana-developers.com/openapi/business-v2.json --- Source spec: `docs/openapi/hapana-business-api.openapi.json` · operationId `get_sessions__id_` · version `v2` ## Security schemes ```json { "partnerKey": { "type": "http", "scheme": "bearer", "description": "The brand API key, e.g. `hk_test_…`. Paste the value only, without `Bearer`." } } ``` # PATCH /public-api/v2/sessions/{id} **Hapana Business API v2** **Base URL**: `https://public-api.hapana-app.com/public-api/v2` ## Summary Update a session ## Description Changes the name, description, capacity or instructor. Setting instructorId replaces the current instructor without emailing anyone; use POST /sessions/{id}/substitute-instructor when members should be told. To move a class to a different time, use Control Center. **Required scope:** `sessions:write` **Tags**: Sessions **Authentication**: ```json [ { "partnerKey": [] } ] ``` ## Parameters | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | id | path | string | yes | | | siteId | query | string | yes | | | Idempotency-Key | header | string | no | Makes the request safe to retry. A retry with the same key returns the first response; reusing a key with a different body is refused with 422. | ## Request Body ```json { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "name": { "type": "string", "minLength": 1, "maxLength": 255 }, "description": { "type": "string", "maxLength": 2000 }, "capacity": { "type": "integer", "minimum": 0, "maximum": 9007199254740991 }, "instructorId": { "type": "string", "format": "uuid", "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$" } } } } } } ``` ## Responses ### 200 — Success ```json { "description": "Success", "content": { "application/json": { "schema": { "type": "object", "properties": { "object": { "type": "string", "const": "session" }, "id": { "type": "string" }, "siteId": { "type": "string", "format": "uuid", "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$" }, "name": { "type": "string" }, "date": { "type": "string", "format": "date", "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$" }, "startTime": { "type": "string", "format": "date-time", "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$" }, "endTime": { "type": "string", "format": "date-time", "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$" }, "status": { "type": "string", "enum": [ "SCHEDULED", "IN_PROGRESS", "COMPLETED", "CANCELLED" ] }, "cancelled": { "type": "boolean" }, "capacity": { "anyOf": [ { "type": "number" }, { "type": "null" } ] }, "bookedCount": { "anyOf": [ { "type": "number" }, { "type": "null" } ] }, "spacesRemaining": { "anyOf": [ { "type": "integer", "minimum": -9007199254740991, "maximum": 9007199254740991 }, { "type": "null" } ] }, "waitlistEnabled": { "type": "boolean" }, "waitlistAvailable": { "type": "boolean" }, "durationMinutes": { "type": "integer", "minimum": -9007199254740991, "maximum": 9007199254740991 }, "sessionTypeId": { "type": "string" }, "scheduleTemplateId": { "anyOf": [ { "type": "string" }, { "type": "null" } ] }, "timezone": { "anyOf": [ { "type": "string" }, { "type": "null" } ] }, "localStartTime": { "anyOf": [ { "type": "string" }, { "type": "null" } ] }, "localEndTime": { "anyOf": [ { "type": "string" }, { "type": "null" } ] }, "locationName": { "anyOf": [ { "type": "string" }, { "type": "null" } ] }, "roomName": { "anyOf": [ { "type": "string" }, { "type": "null" } ] }, "sessionTypeName": { "anyOf": [ { "type": "string" }, { "type": "null" } ] }, "legacyId": { "anyOf": [ { "type": "string" }, { "type": "null" } ] }, "instructors": { "type": "array", "items": { "type": "object", "properties": { "id": { "type": "string" }, "name": { "type": "string" }, "role": { "type": "string" }, "substitutingFor": { "anyOf": [ { "type": "string" }, { "type": "null" } ] } }, "required": [ "id", "name", "role", "substitutingFor" ] } } }, "required": [ "object", "id", "siteId", "name", "date", "startTime", "endTime", "status", "cancelled", "capacity", "bookedCount", "spacesRemaining", "waitlistEnabled", "waitlistAvailable", "durationMinutes", "sessionTypeId", "scheduleTemplateId", "timezone", "localStartTime", "localEndTime", "locationName", "roomName", "sessionTypeName", "legacyId", "instructors" ] } } } } ``` ### 400 — Malformed request ```json { "description": "Malformed request", "content": { "application/json": { "schema": { "type": "object", "properties": { "error": { "type": "string" }, "code": { "type": "string" }, "message": { "type": "string" } }, "required": [ "error" ] } } } } ``` ### 403 — The API key does not have the required scope ```json { "description": "The API key does not have the required scope", "content": { "application/json": { "schema": { "type": "object", "properties": { "error": { "type": "string" }, "code": { "type": "string" }, "message": { "type": "string" } }, "required": [ "error" ] } } } } ``` ### 404 — Not found. A record that belongs to another brand also returns 404. ```json { "description": "Not found. A record that belongs to another brand also returns 404.", "content": { "application/json": { "schema": { "type": "object", "properties": { "error": { "type": "string" }, "code": { "type": "string" }, "message": { "type": "string" } }, "required": [ "error" ] } } } } ``` ### 409 — A request with this Idempotency-Key is still being processed ```json { "description": "A request with this Idempotency-Key is still being processed" } ``` ### 422 — Idempotency-Key reused with a different request body ```json { "description": "Idempotency-Key reused with a different request body" } ``` ## Schemas in scope Refer to the full OpenAPI spec for resolved `$ref` definitions: - https://docs.hapana-developers.com/openapi/business-v2.json --- Source spec: `docs/openapi/hapana-business-api.openapi.json` · operationId `patch_sessions__id_` · version `v2` ## Security schemes ```json { "partnerKey": { "type": "http", "scheme": "bearer", "description": "The brand API key, e.g. `hk_test_…`. Paste the value only, without `Bearer`." } } ``` # POST /public-api/v2/sessions/{id}/cancel **Hapana Business API v2** **Base URL**: `https://public-api.hapana-app.com/public-api/v2` ## Summary Cancel a session ## Description Releases every booking on the session: credits are returned and drop-in payments refunded. scope defaults to this occurrence only. **Required scope:** `sessions:write` **Tags**: Sessions **Authentication**: ```json [ { "partnerKey": [] } ] ``` ## Parameters | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | id | path | string | yes | | | Idempotency-Key | header | string | no | Makes the request safe to retry. A retry with the same key returns the first response; reusing a key with a different body is refused with 422. | ## Request Body ```json { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "siteId": { "type": "string", "format": "uuid", "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$" }, "scope": { "default": "single", "type": "string", "enum": [ "single", "future" ] }, "notifyMembers": { "default": false, "type": "boolean" } }, "required": [ "siteId" ] } } } } ``` ## Responses ### 200 — Success ```json { "description": "Success", "content": { "application/json": { "schema": { "type": "object", "properties": { "object": { "type": "string", "const": "session" }, "id": { "type": "string" }, "siteId": { "type": "string", "format": "uuid", "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$" }, "name": { "type": "string" }, "date": { "type": "string", "format": "date", "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$" }, "startTime": { "type": "string", "format": "date-time", "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$" }, "endTime": { "type": "string", "format": "date-time", "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$" }, "status": { "type": "string", "enum": [ "SCHEDULED", "IN_PROGRESS", "COMPLETED", "CANCELLED" ] }, "cancelled": { "type": "boolean" }, "capacity": { "anyOf": [ { "type": "number" }, { "type": "null" } ] }, "bookedCount": { "anyOf": [ { "type": "number" }, { "type": "null" } ] }, "spacesRemaining": { "anyOf": [ { "type": "integer", "minimum": -9007199254740991, "maximum": 9007199254740991 }, { "type": "null" } ] }, "waitlistEnabled": { "type": "boolean" }, "waitlistAvailable": { "type": "boolean" }, "durationMinutes": { "type": "integer", "minimum": -9007199254740991, "maximum": 9007199254740991 }, "sessionTypeId": { "type": "string" }, "scheduleTemplateId": { "anyOf": [ { "type": "string" }, { "type": "null" } ] }, "timezone": { "anyOf": [ { "type": "string" }, { "type": "null" } ] }, "localStartTime": { "anyOf": [ { "type": "string" }, { "type": "null" } ] }, "localEndTime": { "anyOf": [ { "type": "string" }, { "type": "null" } ] }, "locationName": { "anyOf": [ { "type": "string" }, { "type": "null" } ] }, "roomName": { "anyOf": [ { "type": "string" }, { "type": "null" } ] }, "sessionTypeName": { "anyOf": [ { "type": "string" }, { "type": "null" } ] }, "legacyId": { "anyOf": [ { "type": "string" }, { "type": "null" } ] }, "instructors": { "type": "array", "items": { "type": "object", "properties": { "id": { "type": "string" }, "name": { "type": "string" }, "role": { "type": "string" }, "substitutingFor": { "anyOf": [ { "type": "string" }, { "type": "null" } ] } }, "required": [ "id", "name", "role", "substitutingFor" ] } } }, "required": [ "object", "id", "siteId", "name", "date", "startTime", "endTime", "status", "cancelled", "capacity", "bookedCount", "spacesRemaining", "waitlistEnabled", "waitlistAvailable", "durationMinutes", "sessionTypeId", "scheduleTemplateId", "timezone", "localStartTime", "localEndTime", "locationName", "roomName", "sessionTypeName", "legacyId", "instructors" ] } } } } ``` ### 400 — Malformed request ```json { "description": "Malformed request", "content": { "application/json": { "schema": { "type": "object", "properties": { "error": { "type": "string" }, "code": { "type": "string" }, "message": { "type": "string" } }, "required": [ "error" ] } } } } ``` ### 403 — The API key does not have the required scope ```json { "description": "The API key does not have the required scope", "content": { "application/json": { "schema": { "type": "object", "properties": { "error": { "type": "string" }, "code": { "type": "string" }, "message": { "type": "string" } }, "required": [ "error" ] } } } } ``` ### 404 — Not found. A record that belongs to another brand also returns 404. ```json { "description": "Not found. A record that belongs to another brand also returns 404.", "content": { "application/json": { "schema": { "type": "object", "properties": { "error": { "type": "string" }, "code": { "type": "string" }, "message": { "type": "string" } }, "required": [ "error" ] } } } } ``` ### 409 — A request with this Idempotency-Key is still being processed ```json { "description": "A request with this Idempotency-Key is still being processed" } ``` ### 422 — Idempotency-Key reused with a different request body ```json { "description": "Idempotency-Key reused with a different request body" } ``` ## Schemas in scope Refer to the full OpenAPI spec for resolved `$ref` definitions: - https://docs.hapana-developers.com/openapi/business-v2.json --- Source spec: `docs/openapi/hapana-business-api.openapi.json` · operationId `post_sessions__id__cancel` · version `v2` ## Security schemes ```json { "partnerKey": { "type": "http", "scheme": "bearer", "description": "The brand API key, e.g. `hk_test_…`. Paste the value only, without `Bearer`." } } ``` # POST /public-api/v2/sessions/{id}/instructors **Hapana Business API v2** **Base URL**: `https://public-api.hapana-app.com/public-api/v2` ## Summary Assign an instructor ## Description Fills an empty instructor slot. No one is notified. **Required scope:** `sessions:write` **Tags**: Session staffing **Authentication**: ```json [ { "partnerKey": [] } ] ``` ## Parameters | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | id | path | string | yes | | | Idempotency-Key | header | string | no | Makes the request safe to retry. A retry with the same key returns the first response; reusing a key with a different body is refused with 422. | ## Request Body ```json { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "siteId": { "type": "string", "format": "uuid", "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$" }, "staffUserId": { "type": "string", "format": "uuid", "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$" }, "role": { "default": "INSTRUCTOR", "type": "string", "enum": [ "INSTRUCTOR", "ASSISTANT", "SUBSTITUTE" ] } }, "required": [ "siteId", "staffUserId" ] } } } } ``` ## Responses ### 201 — Success ```json { "description": "Success", "content": { "application/json": { "schema": { "type": "object", "properties": { "staffUserId": { "type": "string", "format": "uuid", "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$" }, "role": { "type": "string" } }, "required": [ "staffUserId", "role" ] } } } } ``` ### 400 — Malformed request ```json { "description": "Malformed request", "content": { "application/json": { "schema": { "type": "object", "properties": { "error": { "type": "string" }, "code": { "type": "string" }, "message": { "type": "string" } }, "required": [ "error" ] } } } } ``` ### 403 — The API key does not have the required scope ```json { "description": "The API key does not have the required scope", "content": { "application/json": { "schema": { "type": "object", "properties": { "error": { "type": "string" }, "code": { "type": "string" }, "message": { "type": "string" } }, "required": [ "error" ] } } } } ``` ### 404 — Not found. A record that belongs to another brand also returns 404. ```json { "description": "Not found. A record that belongs to another brand also returns 404.", "content": { "application/json": { "schema": { "type": "object", "properties": { "error": { "type": "string" }, "code": { "type": "string" }, "message": { "type": "string" } }, "required": [ "error" ] } } } } ``` ### 409 — A request with this Idempotency-Key is still being processed ```json { "description": "A request with this Idempotency-Key is still being processed" } ``` ### 422 — Idempotency-Key reused with a different request body ```json { "description": "Idempotency-Key reused with a different request body" } ``` ## Schemas in scope Refer to the full OpenAPI spec for resolved `$ref` definitions: - https://docs.hapana-developers.com/openapi/business-v2.json --- Source spec: `docs/openapi/hapana-business-api.openapi.json` · operationId `post_sessions__id__instructors` · version `v2` ## Security schemes ```json { "partnerKey": { "type": "http", "scheme": "bearer", "description": "The brand API key, e.g. `hk_test_…`. Paste the value only, without `Bearer`." } } ``` # DELETE /public-api/v2/sessions/{id}/instructors/{staffUserId} **Hapana Business API v2** **Base URL**: `https://public-api.hapana-app.com/public-api/v2` ## Summary Unassign an instructor ## Description Identify the instructor by their staff id. Returns 204 whether or not they were assigned, so retrying after a timeout is safe. **Required scope:** `sessions:write` **Tags**: Session staffing **Authentication**: ```json [ { "partnerKey": [] } ] ``` ## Parameters | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | id | path | string | yes | | | staffUserId | path | string | yes | | | Idempotency-Key | header | string | no | Makes the request safe to retry. A retry with the same key returns the first response; reusing a key with a different body is refused with 422. | ## Responses ### 204 — No content ```json { "description": "No content" } ``` ### 400 — Malformed request ```json { "description": "Malformed request", "content": { "application/json": { "schema": { "type": "object", "properties": { "error": { "type": "string" }, "code": { "type": "string" }, "message": { "type": "string" } }, "required": [ "error" ] } } } } ``` ### 403 — The API key does not have the required scope ```json { "description": "The API key does not have the required scope", "content": { "application/json": { "schema": { "type": "object", "properties": { "error": { "type": "string" }, "code": { "type": "string" }, "message": { "type": "string" } }, "required": [ "error" ] } } } } ``` ### 404 — Not found. A record that belongs to another brand also returns 404. ```json { "description": "Not found. A record that belongs to another brand also returns 404.", "content": { "application/json": { "schema": { "type": "object", "properties": { "error": { "type": "string" }, "code": { "type": "string" }, "message": { "type": "string" } }, "required": [ "error" ] } } } } ``` ### 409 — A request with this Idempotency-Key is still being processed ```json { "description": "A request with this Idempotency-Key is still being processed" } ``` ### 422 — Idempotency-Key reused with a different request body ```json { "description": "Idempotency-Key reused with a different request body" } ``` ## Schemas in scope Refer to the full OpenAPI spec for resolved `$ref` definitions: - https://docs.hapana-developers.com/openapi/business-v2.json --- Source spec: `docs/openapi/hapana-business-api.openapi.json` · operationId `delete_sessions__id__instructors__staffUserId_` · version `v2` ## Security schemes ```json { "partnerKey": { "type": "http", "scheme": "bearer", "description": "The brand API key, e.g. `hk_test_…`. Paste the value only, without `Bearer`." } } ``` # POST /public-api/v2/sessions/{id}/substitute-instructor **Hapana Business API v2** **Base URL**: `https://public-api.hapana-app.com/public-api/v2` ## Summary Substitute the instructor ## Description Swaps one instructor for another and emails every booked member plus both instructors. Both ids are required. **Required scope:** `sessions:write` **Tags**: Session staffing **Authentication**: ```json [ { "partnerKey": [] } ] ``` ## Parameters | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | id | path | string | yes | | | Idempotency-Key | header | string | no | Makes the request safe to retry. A retry with the same key returns the first response; reusing a key with a different body is refused with 422. | ## Request Body ```json { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "siteId": { "type": "string", "format": "uuid", "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$" }, "instructorToRemoveId": { "type": "string", "format": "uuid", "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$" }, "substituteInstructorId": { "type": "string", "format": "uuid", "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$" }, "notifyMembers": { "default": true, "type": "boolean" }, "notifyInstructors": { "default": false, "type": "boolean" } }, "required": [ "siteId", "instructorToRemoveId", "substituteInstructorId" ] } } } } ``` ## Responses ### 200 — Success ```json { "description": "Success", "content": { "application/json": { "schema": { "type": "object", "properties": { "object": { "type": "string", "const": "session" }, "id": { "type": "string" }, "siteId": { "type": "string", "format": "uuid", "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$" }, "name": { "type": "string" }, "date": { "type": "string", "format": "date", "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$" }, "startTime": { "type": "string", "format": "date-time", "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$" }, "endTime": { "type": "string", "format": "date-time", "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$" }, "status": { "type": "string", "enum": [ "SCHEDULED", "IN_PROGRESS", "COMPLETED", "CANCELLED" ] }, "cancelled": { "type": "boolean" }, "capacity": { "anyOf": [ { "type": "number" }, { "type": "null" } ] }, "bookedCount": { "anyOf": [ { "type": "number" }, { "type": "null" } ] }, "spacesRemaining": { "anyOf": [ { "type": "integer", "minimum": -9007199254740991, "maximum": 9007199254740991 }, { "type": "null" } ] }, "waitlistEnabled": { "type": "boolean" }, "waitlistAvailable": { "type": "boolean" }, "durationMinutes": { "type": "integer", "minimum": -9007199254740991, "maximum": 9007199254740991 }, "sessionTypeId": { "type": "string" }, "scheduleTemplateId": { "anyOf": [ { "type": "string" }, { "type": "null" } ] }, "timezone": { "anyOf": [ { "type": "string" }, { "type": "null" } ] }, "localStartTime": { "anyOf": [ { "type": "string" }, { "type": "null" } ] }, "localEndTime": { "anyOf": [ { "type": "string" }, { "type": "null" } ] }, "locationName": { "anyOf": [ { "type": "string" }, { "type": "null" } ] }, "roomName": { "anyOf": [ { "type": "string" }, { "type": "null" } ] }, "sessionTypeName": { "anyOf": [ { "type": "string" }, { "type": "null" } ] }, "legacyId": { "anyOf": [ { "type": "string" }, { "type": "null" } ] }, "instructors": { "type": "array", "items": { "type": "object", "properties": { "id": { "type": "string" }, "name": { "type": "string" }, "role": { "type": "string" }, "substitutingFor": { "anyOf": [ { "type": "string" }, { "type": "null" } ] } }, "required": [ "id", "name", "role", "substitutingFor" ] } } }, "required": [ "object", "id", "siteId", "name", "date", "startTime", "endTime", "status", "cancelled", "capacity", "bookedCount", "spacesRemaining", "waitlistEnabled", "waitlistAvailable", "durationMinutes", "sessionTypeId", "scheduleTemplateId", "timezone", "localStartTime", "localEndTime", "locationName", "roomName", "sessionTypeName", "legacyId", "instructors" ] } } } } ``` ### 400 — Malformed request ```json { "description": "Malformed request", "content": { "application/json": { "schema": { "type": "object", "properties": { "error": { "type": "string" }, "code": { "type": "string" }, "message": { "type": "string" } }, "required": [ "error" ] } } } } ``` ### 403 — The API key does not have the required scope ```json { "description": "The API key does not have the required scope", "content": { "application/json": { "schema": { "type": "object", "properties": { "error": { "type": "string" }, "code": { "type": "string" }, "message": { "type": "string" } }, "required": [ "error" ] } } } } ``` ### 404 — Not found. A record that belongs to another brand also returns 404. ```json { "description": "Not found. A record that belongs to another brand also returns 404.", "content": { "application/json": { "schema": { "type": "object", "properties": { "error": { "type": "string" }, "code": { "type": "string" }, "message": { "type": "string" } }, "required": [ "error" ] } } } } ``` ### 409 — A request with this Idempotency-Key is still being processed ```json { "description": "A request with this Idempotency-Key is still being processed" } ``` ### 422 — Idempotency-Key reused with a different request body ```json { "description": "Idempotency-Key reused with a different request body" } ``` ## Schemas in scope Refer to the full OpenAPI spec for resolved `$ref` definitions: - https://docs.hapana-developers.com/openapi/business-v2.json --- Source spec: `docs/openapi/hapana-business-api.openapi.json` · operationId `post_sessions__id__substitute_instructor` · version `v2` ## Security schemes ```json { "partnerKey": { "type": "http", "scheme": "bearer", "description": "The brand API key, e.g. `hk_test_…`. Paste the value only, without `Bearer`." } } ``` # GET /public-api/v2/clients/search **Hapana Business API v2** **Base URL**: `https://public-api.hapana-app.com/public-api/v2` ## Summary Find a client ## Description Resolve a caller to a client at one site by phone, email, or first and last name. Phone numbers match regardless of format: +61 412 345 678, 0412 345 678 and 412345678 are the same line. Returns up to 10 matches, because one number can belong to a whole family — never assume the first. **Required scope:** `clients:read` **Tags**: Clients **Authentication**: ```json [ { "partnerKey": [] } ] ``` ## Parameters | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | siteId | query | string | yes | | | phone | query | string | no | | | email | query | string | no | | | firstName | query | string | no | | | lastName | query | string | no | | ## Responses ### 200 — Success ```json { "description": "Success", "content": { "application/json": { "schema": { "type": "object", "properties": { "object": { "type": "string", "enum": [ "list" ] }, "data": { "type": "array", "items": { "type": "object", "properties": { "object": { "type": "string", "const": "client" }, "id": { "type": "string" }, "siteId": { "type": "string", "format": "uuid", "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$" }, "firstName": { "anyOf": [ { "type": "string" }, { "type": "null" } ] }, "lastName": { "anyOf": [ { "type": "string" }, { "type": "null" } ] }, "email": { "anyOf": [ { "type": "string" }, { "type": "null" } ] }, "mobile": { "anyOf": [ { "type": "string" }, { "type": "null" } ] }, "fobNumber": { "anyOf": [ { "type": "string" }, { "type": "null" } ] }, "barcode": { "anyOf": [ { "type": "string" }, { "type": "null" } ] }, "dateOfBirth": { "description": "Returned only to a key that also holds clients:pii.", "anyOf": [ { "type": "string", "format": "date", "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$" }, { "type": "null" } ] }, "membershipStatus": { "anyOf": [ { "type": "string" }, { "type": "null" } ] }, "suspended": { "type": "boolean" }, "lastVisitAt": { "anyOf": [ { "type": "string", "format": "date-time", "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$" }, { "type": "null" } ] }, "firstVisitAt": { "anyOf": [ { "type": "string", "format": "date-time", "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$" }, { "type": "null" } ] }, "visitCount": { "type": "integer", "minimum": -9007199254740991, "maximum": 9007199254740991 }, "joinedAt": { "anyOf": [ { "type": "string", "format": "date-time", "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$" }, { "type": "null" } ] }, "communicationPreferences": { "type": "object", "properties": { "email": { "anyOf": [ { "type": "boolean" }, { "type": "null" } ] }, "sms": { "anyOf": [ { "type": "boolean" }, { "type": "null" } ] }, "phone": { "anyOf": [ { "type": "boolean" }, { "type": "null" } ] } }, "required": [ "email", "sms", "phone" ] }, "source": { "anyOf": [ { "type": "string", "enum": [ "HAPANA", "WEB", "APP", "STAFF", "KIOSK", "AGGREGATOR", "MIGRATION_IMPORTED", "APP_WEB_SIGNUP", "BUSINESS_ADDED", "EMBED_CREATED", "THIRD_PARTY", "CLIENT_REFERRAL", "SOCIAL_MEDIA" ] }, { "type": "null" } ] }, "createdAt": { "type": "string", "format": "date-time", "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$" }, "updatedAt": { "type": "string", "format": "date-time", "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$" } }, "required": [ "object", "id", "siteId", "firstName", "lastName", "email", "mobile", "membershipStatus", "suspended", "lastVisitAt", "firstVisitAt", "visitCount", "joinedAt", "communicationPreferences", "source", "createdAt", "updatedAt" ] } }, "hasMore": { "type": "boolean" }, "nextCursor": { "type": [ "string", "null" ] }, "url": { "type": "string" } }, "required": [ "object", "data", "hasMore", "url" ] } } } } ``` ### 400 — Malformed request ```json { "description": "Malformed request", "content": { "application/json": { "schema": { "type": "object", "properties": { "error": { "type": "string" }, "code": { "type": "string" }, "message": { "type": "string" } }, "required": [ "error" ] } } } } ``` ### 403 — The API key does not have the required scope ```json { "description": "The API key does not have the required scope", "content": { "application/json": { "schema": { "type": "object", "properties": { "error": { "type": "string" }, "code": { "type": "string" }, "message": { "type": "string" } }, "required": [ "error" ] } } } } ``` ### 404 — Not found. A record that belongs to another brand also returns 404. ```json { "description": "Not found. A record that belongs to another brand also returns 404.", "content": { "application/json": { "schema": { "type": "object", "properties": { "error": { "type": "string" }, "code": { "type": "string" }, "message": { "type": "string" } }, "required": [ "error" ] } } } } ``` ## Schemas in scope Refer to the full OpenAPI spec for resolved `$ref` definitions: - https://docs.hapana-developers.com/openapi/business-v2.json --- Source spec: `docs/openapi/hapana-business-api.openapi.json` · operationId `get_clients_search` · version `v2` ## Security schemes ```json { "partnerKey": { "type": "http", "scheme": "bearer", "description": "The brand API key, e.g. `hk_test_…`. Paste the value only, without `Bearer`." } } ``` # GET /public-api/v2/clients **Hapana Business API v2** **Base URL**: `https://public-api.hapana-app.com/public-api/v2` ## Summary List clients at a site ## Description Cursor-paginated. lastVisitBefore (a calendar day at the site) narrows to members whose last visit was before that day, and excludes anyone who has never visited; combine with membershipStatus to build a reactivation segment. updatedAfter (an ISO timestamp with offset) returns only clients whose details or membership at this site changed after it — store the time a sync started as the next watermark. Up to 500 per page. Always honour communicationPreferences before contacting anyone. **Required scope:** `clients:read` **Tags**: Clients **Authentication**: ```json [ { "partnerKey": [] } ] ``` ## Parameters | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | cursor | query | string | no | | | limit | query | integer | no | | | siteId | query | string | yes | | | lastVisitBefore | query | string | no | | | membershipStatus | query | string | no | | | updatedAfter | query | string | no | | ## Responses ### 200 — Success ```json { "description": "Success", "content": { "application/json": { "schema": { "type": "object", "properties": { "object": { "type": "string", "enum": [ "list" ] }, "data": { "type": "array", "items": { "type": "object", "properties": { "object": { "type": "string", "const": "client" }, "id": { "type": "string" }, "siteId": { "type": "string", "format": "uuid", "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$" }, "firstName": { "anyOf": [ { "type": "string" }, { "type": "null" } ] }, "lastName": { "anyOf": [ { "type": "string" }, { "type": "null" } ] }, "email": { "anyOf": [ { "type": "string" }, { "type": "null" } ] }, "mobile": { "anyOf": [ { "type": "string" }, { "type": "null" } ] }, "fobNumber": { "anyOf": [ { "type": "string" }, { "type": "null" } ] }, "barcode": { "anyOf": [ { "type": "string" }, { "type": "null" } ] }, "dateOfBirth": { "description": "Returned only to a key that also holds clients:pii.", "anyOf": [ { "type": "string", "format": "date", "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$" }, { "type": "null" } ] }, "membershipStatus": { "anyOf": [ { "type": "string" }, { "type": "null" } ] }, "suspended": { "type": "boolean" }, "lastVisitAt": { "anyOf": [ { "type": "string", "format": "date-time", "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$" }, { "type": "null" } ] }, "firstVisitAt": { "anyOf": [ { "type": "string", "format": "date-time", "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$" }, { "type": "null" } ] }, "visitCount": { "type": "integer", "minimum": -9007199254740991, "maximum": 9007199254740991 }, "joinedAt": { "anyOf": [ { "type": "string", "format": "date-time", "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$" }, { "type": "null" } ] }, "communicationPreferences": { "type": "object", "properties": { "email": { "anyOf": [ { "type": "boolean" }, { "type": "null" } ] }, "sms": { "anyOf": [ { "type": "boolean" }, { "type": "null" } ] }, "phone": { "anyOf": [ { "type": "boolean" }, { "type": "null" } ] } }, "required": [ "email", "sms", "phone" ] }, "source": { "anyOf": [ { "type": "string", "enum": [ "HAPANA", "WEB", "APP", "STAFF", "KIOSK", "AGGREGATOR", "MIGRATION_IMPORTED", "APP_WEB_SIGNUP", "BUSINESS_ADDED", "EMBED_CREATED", "THIRD_PARTY", "CLIENT_REFERRAL", "SOCIAL_MEDIA" ] }, { "type": "null" } ] }, "createdAt": { "type": "string", "format": "date-time", "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$" }, "updatedAt": { "type": "string", "format": "date-time", "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$" } }, "required": [ "object", "id", "siteId", "firstName", "lastName", "email", "mobile", "membershipStatus", "suspended", "lastVisitAt", "firstVisitAt", "visitCount", "joinedAt", "communicationPreferences", "source", "createdAt", "updatedAt" ] } }, "hasMore": { "type": "boolean" }, "nextCursor": { "type": [ "string", "null" ] }, "url": { "type": "string" } }, "required": [ "object", "data", "hasMore", "url" ] } } } } ``` ### 400 — Malformed request ```json { "description": "Malformed request", "content": { "application/json": { "schema": { "type": "object", "properties": { "error": { "type": "string" }, "code": { "type": "string" }, "message": { "type": "string" } }, "required": [ "error" ] } } } } ``` ### 403 — The API key does not have the required scope ```json { "description": "The API key does not have the required scope", "content": { "application/json": { "schema": { "type": "object", "properties": { "error": { "type": "string" }, "code": { "type": "string" }, "message": { "type": "string" } }, "required": [ "error" ] } } } } ``` ### 404 — Not found. A record that belongs to another brand also returns 404. ```json { "description": "Not found. A record that belongs to another brand also returns 404.", "content": { "application/json": { "schema": { "type": "object", "properties": { "error": { "type": "string" }, "code": { "type": "string" }, "message": { "type": "string" } }, "required": [ "error" ] } } } } ``` ## Schemas in scope Refer to the full OpenAPI spec for resolved `$ref` definitions: - https://docs.hapana-developers.com/openapi/business-v2.json --- Source spec: `docs/openapi/hapana-business-api.openapi.json` · operationId `get_clients` · version `v2` ## Security schemes ```json { "partnerKey": { "type": "http", "scheme": "bearer", "description": "The brand API key, e.g. `hk_test_…`. Paste the value only, without `Bearer`." } } ``` # POST /public-api/v2/clients **Hapana Business API v2** **Base URL**: `https://public-api.hapana-app.com/public-api/v2` ## Summary Create a client ## Description Email is required. A client already at this site with the same email, or the same phone and name, is refused with 409 DUPLICATE_CLIENT and the existing clientId. The response lists pendingAgreements the client must sign before a booking is accepted. **Required scope:** `clients:write` **Tags**: Clients **Authentication**: ```json [ { "partnerKey": [] } ] ``` ## Parameters | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | Idempotency-Key | header | string | no | Makes the request safe to retry. A retry with the same key returns the first response; reusing a key with a different body is refused with 422. | ## Request Body ```json { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "siteId": { "type": "string", "format": "uuid", "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$" }, "firstName": { "type": "string", "minLength": 1, "maxLength": 50 }, "lastName": { "type": "string", "minLength": 1, "maxLength": 50 }, "email": { "type": "string", "maxLength": 50, "format": "email", "pattern": "^(?!\\.)(?!.*\\.\\.)([A-Za-z0-9_'+\\-\\.]*)[A-Za-z0-9_+-]@([A-Za-z0-9][A-Za-z0-9\\-]*\\.)+[A-Za-z]{2,}$" }, "phone": { "type": "string", "minLength": 1, "maxLength": 20 }, "dateOfBirth": { "type": "string", "format": "date", "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$" }, "sourceLabel": { "type": "string", "minLength": 1, "maxLength": 100 }, "communicationPreferences": { "type": "object", "properties": { "email": { "type": "boolean" }, "sms": { "type": "boolean" }, "phone": { "type": "boolean" } } } }, "required": [ "siteId", "firstName", "lastName", "email" ] } } } } ``` ## Responses ### 201 — Success ```json { "description": "Success", "content": { "application/json": { "schema": { "type": "object", "properties": { "object": { "type": "string", "const": "client" }, "id": { "type": "string" }, "siteId": { "type": "string", "format": "uuid", "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$" }, "firstName": { "anyOf": [ { "type": "string" }, { "type": "null" } ] }, "lastName": { "anyOf": [ { "type": "string" }, { "type": "null" } ] }, "email": { "anyOf": [ { "type": "string" }, { "type": "null" } ] }, "mobile": { "anyOf": [ { "type": "string" }, { "type": "null" } ] }, "fobNumber": { "anyOf": [ { "type": "string" }, { "type": "null" } ] }, "barcode": { "anyOf": [ { "type": "string" }, { "type": "null" } ] }, "dateOfBirth": { "description": "Returned only to a key that also holds clients:pii.", "anyOf": [ { "type": "string", "format": "date", "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$" }, { "type": "null" } ] }, "membershipStatus": { "anyOf": [ { "type": "string" }, { "type": "null" } ] }, "suspended": { "type": "boolean" }, "lastVisitAt": { "anyOf": [ { "type": "string", "format": "date-time", "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$" }, { "type": "null" } ] }, "firstVisitAt": { "anyOf": [ { "type": "string", "format": "date-time", "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$" }, { "type": "null" } ] }, "visitCount": { "type": "integer", "minimum": -9007199254740991, "maximum": 9007199254740991 }, "joinedAt": { "anyOf": [ { "type": "string", "format": "date-time", "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$" }, { "type": "null" } ] }, "communicationPreferences": { "type": "object", "properties": { "email": { "anyOf": [ { "type": "boolean" }, { "type": "null" } ] }, "sms": { "anyOf": [ { "type": "boolean" }, { "type": "null" } ] }, "phone": { "anyOf": [ { "type": "boolean" }, { "type": "null" } ] } }, "required": [ "email", "sms", "phone" ] }, "source": { "anyOf": [ { "type": "string", "enum": [ "HAPANA", "WEB", "APP", "STAFF", "KIOSK", "AGGREGATOR", "MIGRATION_IMPORTED", "APP_WEB_SIGNUP", "BUSINESS_ADDED", "EMBED_CREATED", "THIRD_PARTY", "CLIENT_REFERRAL", "SOCIAL_MEDIA" ] }, { "type": "null" } ] }, "createdAt": { "type": "string", "format": "date-time", "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$" }, "updatedAt": { "type": "string", "format": "date-time", "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$" }, "pendingAgreements": { "type": "array", "items": { "type": "object", "properties": { "id": { "type": "string" }, "name": { "type": "string" }, "type": { "type": "string" }, "requiresSignature": { "type": "boolean" } }, "required": [ "id", "name", "type", "requiresSignature" ] } } }, "required": [ "object", "id", "siteId", "firstName", "lastName", "email", "mobile", "membershipStatus", "suspended", "lastVisitAt", "firstVisitAt", "visitCount", "joinedAt", "communicationPreferences", "source", "createdAt", "updatedAt", "pendingAgreements" ] } } } } ``` ### 400 — Malformed request ```json { "description": "Malformed request", "content": { "application/json": { "schema": { "type": "object", "properties": { "error": { "type": "string" }, "code": { "type": "string" }, "message": { "type": "string" } }, "required": [ "error" ] } } } } ``` ### 403 — The API key does not have the required scope ```json { "description": "The API key does not have the required scope", "content": { "application/json": { "schema": { "type": "object", "properties": { "error": { "type": "string" }, "code": { "type": "string" }, "message": { "type": "string" } }, "required": [ "error" ] } } } } ``` ### 404 — Not found. A record that belongs to another brand also returns 404. ```json { "description": "Not found. A record that belongs to another brand also returns 404.", "content": { "application/json": { "schema": { "type": "object", "properties": { "error": { "type": "string" }, "code": { "type": "string" }, "message": { "type": "string" } }, "required": [ "error" ] } } } } ``` ### 409 — A request with this Idempotency-Key is still being processed ```json { "description": "A request with this Idempotency-Key is still being processed" } ``` ### 422 — Idempotency-Key reused with a different request body ```json { "description": "Idempotency-Key reused with a different request body" } ``` ## Schemas in scope Refer to the full OpenAPI spec for resolved `$ref` definitions: - https://docs.hapana-developers.com/openapi/business-v2.json --- Source spec: `docs/openapi/hapana-business-api.openapi.json` · operationId `post_clients` · version `v2` ## Security schemes ```json { "partnerKey": { "type": "http", "scheme": "bearer", "description": "The brand API key, e.g. `hk_test_…`. Paste the value only, without `Bearer`." } } ``` # GET /public-api/v2/clients/by-barcode **Hapana Business API v2** **Base URL**: `https://public-api.hapana-app.com/public-api/v2` ## Summary Find a client by barcode ## Description Finds the member at this site from a scanned card or app barcode. **Required scope:** `clients:read` **Tags**: Clients **Authentication**: ```json [ { "partnerKey": [] } ] ``` ## Parameters | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | siteId | query | string | yes | | | barcode | query | string | yes | | ## Responses ### 200 — Success ```json { "description": "Success", "content": { "application/json": { "schema": { "type": "object", "properties": { "object": { "type": "string", "const": "client" }, "id": { "type": "string" }, "siteId": { "type": "string", "format": "uuid", "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$" }, "firstName": { "anyOf": [ { "type": "string" }, { "type": "null" } ] }, "lastName": { "anyOf": [ { "type": "string" }, { "type": "null" } ] }, "email": { "anyOf": [ { "type": "string" }, { "type": "null" } ] }, "mobile": { "anyOf": [ { "type": "string" }, { "type": "null" } ] }, "fobNumber": { "anyOf": [ { "type": "string" }, { "type": "null" } ] }, "barcode": { "anyOf": [ { "type": "string" }, { "type": "null" } ] }, "dateOfBirth": { "description": "Returned only to a key that also holds clients:pii.", "anyOf": [ { "type": "string", "format": "date", "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$" }, { "type": "null" } ] }, "membershipStatus": { "anyOf": [ { "type": "string" }, { "type": "null" } ] }, "suspended": { "type": "boolean" }, "lastVisitAt": { "anyOf": [ { "type": "string", "format": "date-time", "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$" }, { "type": "null" } ] }, "firstVisitAt": { "anyOf": [ { "type": "string", "format": "date-time", "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$" }, { "type": "null" } ] }, "visitCount": { "type": "integer", "minimum": -9007199254740991, "maximum": 9007199254740991 }, "joinedAt": { "anyOf": [ { "type": "string", "format": "date-time", "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$" }, { "type": "null" } ] }, "communicationPreferences": { "type": "object", "properties": { "email": { "anyOf": [ { "type": "boolean" }, { "type": "null" } ] }, "sms": { "anyOf": [ { "type": "boolean" }, { "type": "null" } ] }, "phone": { "anyOf": [ { "type": "boolean" }, { "type": "null" } ] } }, "required": [ "email", "sms", "phone" ] }, "source": { "anyOf": [ { "type": "string", "enum": [ "HAPANA", "WEB", "APP", "STAFF", "KIOSK", "AGGREGATOR", "MIGRATION_IMPORTED", "APP_WEB_SIGNUP", "BUSINESS_ADDED", "EMBED_CREATED", "THIRD_PARTY", "CLIENT_REFERRAL", "SOCIAL_MEDIA" ] }, { "type": "null" } ] }, "createdAt": { "type": "string", "format": "date-time", "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$" }, "updatedAt": { "type": "string", "format": "date-time", "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$" } }, "required": [ "object", "id", "siteId", "firstName", "lastName", "email", "mobile", "membershipStatus", "suspended", "lastVisitAt", "firstVisitAt", "visitCount", "joinedAt", "communicationPreferences", "source", "createdAt", "updatedAt" ] } } } } ``` ### 400 — Malformed request ```json { "description": "Malformed request", "content": { "application/json": { "schema": { "type": "object", "properties": { "error": { "type": "string" }, "code": { "type": "string" }, "message": { "type": "string" } }, "required": [ "error" ] } } } } ``` ### 403 — The API key does not have the required scope ```json { "description": "The API key does not have the required scope", "content": { "application/json": { "schema": { "type": "object", "properties": { "error": { "type": "string" }, "code": { "type": "string" }, "message": { "type": "string" } }, "required": [ "error" ] } } } } ``` ### 404 — Not found. A record that belongs to another brand also returns 404. ```json { "description": "Not found. A record that belongs to another brand also returns 404.", "content": { "application/json": { "schema": { "type": "object", "properties": { "error": { "type": "string" }, "code": { "type": "string" }, "message": { "type": "string" } }, "required": [ "error" ] } } } } ``` ## Schemas in scope Refer to the full OpenAPI spec for resolved `$ref` definitions: - https://docs.hapana-developers.com/openapi/business-v2.json --- Source spec: `docs/openapi/hapana-business-api.openapi.json` · operationId `get_clients_by_barcode` · version `v2` ## Security schemes ```json { "partnerKey": { "type": "http", "scheme": "bearer", "description": "The brand API key, e.g. `hk_test_…`. Paste the value only, without `Bearer`." } } ``` # GET /public-api/v2/clients/{id} **Hapana Business API v2** **Base URL**: `https://public-api.hapana-app.com/public-api/v2` ## Summary Retrieve a client ## Description **Required scope:** `clients:read` **Tags**: Clients **Authentication**: ```json [ { "partnerKey": [] } ] ``` ## Parameters | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | id | path | string | yes | | | siteId | query | string | yes | | ## Responses ### 200 — Success ```json { "description": "Success", "content": { "application/json": { "schema": { "type": "object", "properties": { "object": { "type": "string", "const": "client" }, "id": { "type": "string" }, "siteId": { "type": "string", "format": "uuid", "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$" }, "firstName": { "anyOf": [ { "type": "string" }, { "type": "null" } ] }, "lastName": { "anyOf": [ { "type": "string" }, { "type": "null" } ] }, "email": { "anyOf": [ { "type": "string" }, { "type": "null" } ] }, "mobile": { "anyOf": [ { "type": "string" }, { "type": "null" } ] }, "fobNumber": { "anyOf": [ { "type": "string" }, { "type": "null" } ] }, "barcode": { "anyOf": [ { "type": "string" }, { "type": "null" } ] }, "dateOfBirth": { "description": "Returned only to a key that also holds clients:pii.", "anyOf": [ { "type": "string", "format": "date", "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$" }, { "type": "null" } ] }, "membershipStatus": { "anyOf": [ { "type": "string" }, { "type": "null" } ] }, "suspended": { "type": "boolean" }, "lastVisitAt": { "anyOf": [ { "type": "string", "format": "date-time", "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$" }, { "type": "null" } ] }, "firstVisitAt": { "anyOf": [ { "type": "string", "format": "date-time", "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$" }, { "type": "null" } ] }, "visitCount": { "type": "integer", "minimum": -9007199254740991, "maximum": 9007199254740991 }, "joinedAt": { "anyOf": [ { "type": "string", "format": "date-time", "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$" }, { "type": "null" } ] }, "communicationPreferences": { "type": "object", "properties": { "email": { "anyOf": [ { "type": "boolean" }, { "type": "null" } ] }, "sms": { "anyOf": [ { "type": "boolean" }, { "type": "null" } ] }, "phone": { "anyOf": [ { "type": "boolean" }, { "type": "null" } ] } }, "required": [ "email", "sms", "phone" ] }, "source": { "anyOf": [ { "type": "string", "enum": [ "HAPANA", "WEB", "APP", "STAFF", "KIOSK", "AGGREGATOR", "MIGRATION_IMPORTED", "APP_WEB_SIGNUP", "BUSINESS_ADDED", "EMBED_CREATED", "THIRD_PARTY", "CLIENT_REFERRAL", "SOCIAL_MEDIA" ] }, { "type": "null" } ] }, "createdAt": { "type": "string", "format": "date-time", "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$" }, "updatedAt": { "type": "string", "format": "date-time", "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$" } }, "required": [ "object", "id", "siteId", "firstName", "lastName", "email", "mobile", "membershipStatus", "suspended", "lastVisitAt", "firstVisitAt", "visitCount", "joinedAt", "communicationPreferences", "source", "createdAt", "updatedAt" ] } } } } ``` ### 400 — Malformed request ```json { "description": "Malformed request", "content": { "application/json": { "schema": { "type": "object", "properties": { "error": { "type": "string" }, "code": { "type": "string" }, "message": { "type": "string" } }, "required": [ "error" ] } } } } ``` ### 403 — The API key does not have the required scope ```json { "description": "The API key does not have the required scope", "content": { "application/json": { "schema": { "type": "object", "properties": { "error": { "type": "string" }, "code": { "type": "string" }, "message": { "type": "string" } }, "required": [ "error" ] } } } } ``` ### 404 — Not found. A record that belongs to another brand also returns 404. ```json { "description": "Not found. A record that belongs to another brand also returns 404.", "content": { "application/json": { "schema": { "type": "object", "properties": { "error": { "type": "string" }, "code": { "type": "string" }, "message": { "type": "string" } }, "required": [ "error" ] } } } } ``` ## Schemas in scope Refer to the full OpenAPI spec for resolved `$ref` definitions: - https://docs.hapana-developers.com/openapi/business-v2.json --- Source spec: `docs/openapi/hapana-business-api.openapi.json` · operationId `get_clients__id_` · version `v2` ## Security schemes ```json { "partnerKey": { "type": "http", "scheme": "bearer", "description": "The brand API key, e.g. `hk_test_…`. Paste the value only, without `Bearer`." } } ``` # POST /public-api/v2/clients/{id} **Hapana Business API v2** **Base URL**: `https://public-api.hapana-app.com/public-api/v2` ## Summary Update a client ## Description Contact details and communication preferences. Set a channel to false to record an opt-out. **Required scope:** `clients:write` **Tags**: Clients **Authentication**: ```json [ { "partnerKey": [] } ] ``` ## Parameters | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | id | path | string | yes | | | siteId | query | string | yes | | | Idempotency-Key | header | string | no | Makes the request safe to retry. A retry with the same key returns the first response; reusing a key with a different body is refused with 422. | ## Request Body ```json { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "firstName": { "type": "string", "minLength": 1, "maxLength": 50 }, "lastName": { "type": "string", "minLength": 1, "maxLength": 50 }, "email": { "type": "string", "maxLength": 50, "format": "email", "pattern": "^(?!\\.)(?!.*\\.\\.)([A-Za-z0-9_'+\\-\\.]*)[A-Za-z0-9_+-]@([A-Za-z0-9][A-Za-z0-9\\-]*\\.)+[A-Za-z]{2,}$" }, "phone": { "type": "string", "minLength": 1, "maxLength": 20 }, "dateOfBirth": { "type": "string", "format": "date", "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$" }, "fobNumber": { "anyOf": [ { "type": "string", "maxLength": 50 }, { "type": "null" } ] }, "communicationPreferences": { "type": "object", "properties": { "email": { "type": "boolean" }, "sms": { "type": "boolean" }, "phone": { "type": "boolean" } } } } } } } } ``` ## Responses ### 200 — Success ```json { "description": "Success", "content": { "application/json": { "schema": { "type": "object", "properties": { "object": { "type": "string", "const": "client" }, "id": { "type": "string" }, "siteId": { "type": "string", "format": "uuid", "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$" }, "firstName": { "anyOf": [ { "type": "string" }, { "type": "null" } ] }, "lastName": { "anyOf": [ { "type": "string" }, { "type": "null" } ] }, "email": { "anyOf": [ { "type": "string" }, { "type": "null" } ] }, "mobile": { "anyOf": [ { "type": "string" }, { "type": "null" } ] }, "fobNumber": { "anyOf": [ { "type": "string" }, { "type": "null" } ] }, "barcode": { "anyOf": [ { "type": "string" }, { "type": "null" } ] }, "dateOfBirth": { "description": "Returned only to a key that also holds clients:pii.", "anyOf": [ { "type": "string", "format": "date", "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$" }, { "type": "null" } ] }, "membershipStatus": { "anyOf": [ { "type": "string" }, { "type": "null" } ] }, "suspended": { "type": "boolean" }, "lastVisitAt": { "anyOf": [ { "type": "string", "format": "date-time", "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$" }, { "type": "null" } ] }, "firstVisitAt": { "anyOf": [ { "type": "string", "format": "date-time", "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$" }, { "type": "null" } ] }, "visitCount": { "type": "integer", "minimum": -9007199254740991, "maximum": 9007199254740991 }, "joinedAt": { "anyOf": [ { "type": "string", "format": "date-time", "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$" }, { "type": "null" } ] }, "communicationPreferences": { "type": "object", "properties": { "email": { "anyOf": [ { "type": "boolean" }, { "type": "null" } ] }, "sms": { "anyOf": [ { "type": "boolean" }, { "type": "null" } ] }, "phone": { "anyOf": [ { "type": "boolean" }, { "type": "null" } ] } }, "required": [ "email", "sms", "phone" ] }, "source": { "anyOf": [ { "type": "string", "enum": [ "HAPANA", "WEB", "APP", "STAFF", "KIOSK", "AGGREGATOR", "MIGRATION_IMPORTED", "APP_WEB_SIGNUP", "BUSINESS_ADDED", "EMBED_CREATED", "THIRD_PARTY", "CLIENT_REFERRAL", "SOCIAL_MEDIA" ] }, { "type": "null" } ] }, "createdAt": { "type": "string", "format": "date-time", "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$" }, "updatedAt": { "type": "string", "format": "date-time", "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$" } }, "required": [ "object", "id", "siteId", "firstName", "lastName", "email", "mobile", "membershipStatus", "suspended", "lastVisitAt", "firstVisitAt", "visitCount", "joinedAt", "communicationPreferences", "source", "createdAt", "updatedAt" ] } } } } ``` ### 400 — Malformed request ```json { "description": "Malformed request", "content": { "application/json": { "schema": { "type": "object", "properties": { "error": { "type": "string" }, "code": { "type": "string" }, "message": { "type": "string" } }, "required": [ "error" ] } } } } ``` ### 403 — The API key does not have the required scope ```json { "description": "The API key does not have the required scope", "content": { "application/json": { "schema": { "type": "object", "properties": { "error": { "type": "string" }, "code": { "type": "string" }, "message": { "type": "string" } }, "required": [ "error" ] } } } } ``` ### 404 — Not found. A record that belongs to another brand also returns 404. ```json { "description": "Not found. A record that belongs to another brand also returns 404.", "content": { "application/json": { "schema": { "type": "object", "properties": { "error": { "type": "string" }, "code": { "type": "string" }, "message": { "type": "string" } }, "required": [ "error" ] } } } } ``` ### 409 — A request with this Idempotency-Key is still being processed ```json { "description": "A request with this Idempotency-Key is still being processed" } ``` ### 422 — Idempotency-Key reused with a different request body ```json { "description": "Idempotency-Key reused with a different request body" } ``` ## Schemas in scope Refer to the full OpenAPI spec for resolved `$ref` definitions: - https://docs.hapana-developers.com/openapi/business-v2.json --- Source spec: `docs/openapi/hapana-business-api.openapi.json` · operationId `post_clients__id_` · version `v2` ## Security schemes ```json { "partnerKey": { "type": "http", "scheme": "bearer", "description": "The brand API key, e.g. `hk_test_…`. Paste the value only, without `Bearer`." } } ``` # POST /public-api/v2/clients/{id}/notes **Hapana Business API v2** **Base URL**: `https://public-api.hapana-app.com/public-api/v2` ## Summary Add a note to a client ## Description Records a call outcome or decline reason, visible to the studio team. **Required scope:** `clients:write` **Tags**: Clients **Authentication**: ```json [ { "partnerKey": [] } ] ``` ## Parameters | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | id | path | string | yes | | | Idempotency-Key | header | string | no | Makes the request safe to retry. A retry with the same key returns the first response; reusing a key with a different body is refused with 422. | ## Request Body ```json { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "siteId": { "type": "string", "format": "uuid", "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$" }, "content": { "type": "string", "minLength": 1, "maxLength": 10000 } }, "required": [ "siteId", "content" ] } } } } ``` ## Responses ### 201 — Success ```json { "description": "Success", "content": { "application/json": { "schema": { "type": "object", "properties": { "object": { "type": "string", "const": "note" }, "id": { "type": "string" }, "clientId": { "type": "string" }, "createdAt": { "type": "string", "format": "date-time", "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$" } }, "required": [ "object", "id", "clientId", "createdAt" ] } } } } ``` ### 400 — Malformed request ```json { "description": "Malformed request", "content": { "application/json": { "schema": { "type": "object", "properties": { "error": { "type": "string" }, "code": { "type": "string" }, "message": { "type": "string" } }, "required": [ "error" ] } } } } ``` ### 403 — The API key does not have the required scope ```json { "description": "The API key does not have the required scope", "content": { "application/json": { "schema": { "type": "object", "properties": { "error": { "type": "string" }, "code": { "type": "string" }, "message": { "type": "string" } }, "required": [ "error" ] } } } } ``` ### 404 — Not found. A record that belongs to another brand also returns 404. ```json { "description": "Not found. A record that belongs to another brand also returns 404.", "content": { "application/json": { "schema": { "type": "object", "properties": { "error": { "type": "string" }, "code": { "type": "string" }, "message": { "type": "string" } }, "required": [ "error" ] } } } } ``` ### 409 — A request with this Idempotency-Key is still being processed ```json { "description": "A request with this Idempotency-Key is still being processed" } ``` ### 422 — Idempotency-Key reused with a different request body ```json { "description": "Idempotency-Key reused with a different request body" } ``` ## Schemas in scope Refer to the full OpenAPI spec for resolved `$ref` definitions: - https://docs.hapana-developers.com/openapi/business-v2.json --- Source spec: `docs/openapi/hapana-business-api.openapi.json` · operationId `post_clients__id__notes` · version `v2` ## Security schemes ```json { "partnerKey": { "type": "http", "scheme": "bearer", "description": "The brand API key, e.g. `hk_test_…`. Paste the value only, without `Bearer`." } } ``` # GET /public-api/v2/bookings **Hapana Business API v2** **Base URL**: `https://public-api.hapana-app.com/public-api/v2` ## Summary List a client's bookings ## Description startDate and endDate are inclusive calendar days at the site and bound when the session starts. A booking just created is visible here immediately. **Required scope:** `bookings:read` **Tags**: Bookings **Authentication**: ```json [ { "partnerKey": [] } ] ``` ## Parameters | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | cursor | query | string | no | | | limit | query | integer | no | | | siteId | query | string | yes | | | clientId | query | string | yes | | | startDate | query | string | yes | | | endDate | query | string | yes | | | status | query | string | no | | ## Responses ### 200 — Success ```json { "description": "Success", "content": { "application/json": { "schema": { "type": "object", "properties": { "object": { "type": "string", "enum": [ "list" ] }, "data": { "type": "array", "items": { "type": "object", "properties": { "object": { "type": "string", "const": "booking" }, "id": { "type": "string" }, "siteId": { "type": "string" }, "clientId": { "type": "string" }, "status": { "type": "string", "enum": [ "PENDING", "CONFIRMED", "DECLINED", "CANCELLED", "LATE_CANCELLED", "ATTENDED", "NO_SHOW", "INVITED", "EXPIRED", "REJECTED" ] }, "cancelled": { "type": "boolean" }, "paymentSource": { "anyOf": [ { "type": "string", "enum": [ "CREDIT", "FREE", "DROP_IN", "BILLING", "ADMIN", "AGGREGATOR", "IMPORT", "GUEST_PASS" ] }, { "type": "null" } ] }, "paymentStatus": { "anyOf": [ { "type": "string", "enum": [ "paid", "pending" ] }, { "type": "null" } ] }, "spotId": { "anyOf": [ { "type": "integer", "minimum": -9007199254740991, "maximum": 9007199254740991 }, { "type": "null" } ] }, "createdAt": { "type": "string", "format": "date-time", "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$" }, "cancelledAt": { "anyOf": [ { "type": "string", "format": "date-time", "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$" }, { "type": "null" } ] }, "session": { "type": "object", "properties": { "id": { "type": "string" }, "name": { "type": "string" }, "category": { "type": "string", "enum": [ "CLASS", "PRIVATE", "WORKSHOP" ] }, "status": { "type": "string", "enum": [ "SCHEDULED", "IN_PROGRESS", "COMPLETED", "CANCELLED" ] }, "startTime": { "type": "string", "format": "date-time", "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$" }, "endTime": { "type": "string", "format": "date-time", "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$" }, "timezone": { "anyOf": [ { "type": "string" }, { "type": "null" } ] }, "localStartTime": { "anyOf": [ { "type": "string" }, { "type": "null" } ] }, "localEndTime": { "anyOf": [ { "type": "string" }, { "type": "null" } ] } }, "required": [ "id", "name", "category", "status", "startTime", "endTime", "timezone", "localStartTime", "localEndTime" ] } }, "required": [ "object", "id", "siteId", "clientId", "status", "cancelled", "paymentSource", "paymentStatus", "spotId", "createdAt", "cancelledAt", "session" ] } }, "hasMore": { "type": "boolean" }, "nextCursor": { "type": [ "string", "null" ] }, "url": { "type": "string" } }, "required": [ "object", "data", "hasMore", "url" ] } } } } ``` ### 400 — Malformed request ```json { "description": "Malformed request", "content": { "application/json": { "schema": { "type": "object", "properties": { "error": { "type": "string" }, "code": { "type": "string" }, "message": { "type": "string" } }, "required": [ "error" ] } } } } ``` ### 403 — The API key does not have the required scope ```json { "description": "The API key does not have the required scope", "content": { "application/json": { "schema": { "type": "object", "properties": { "error": { "type": "string" }, "code": { "type": "string" }, "message": { "type": "string" } }, "required": [ "error" ] } } } } ``` ### 404 — Not found. A record that belongs to another brand also returns 404. ```json { "description": "Not found. A record that belongs to another brand also returns 404.", "content": { "application/json": { "schema": { "type": "object", "properties": { "error": { "type": "string" }, "code": { "type": "string" }, "message": { "type": "string" } }, "required": [ "error" ] } } } } ``` ## Schemas in scope Refer to the full OpenAPI spec for resolved `$ref` definitions: - https://docs.hapana-developers.com/openapi/business-v2.json --- Source spec: `docs/openapi/hapana-business-api.openapi.json` · operationId `get_bookings` · version `v2` ## Security schemes ```json { "partnerKey": { "type": "http", "scheme": "bearer", "description": "The brand API key, e.g. `hk_test_…`. Paste the value only, without `Bearer`." } } ``` # POST /public-api/v2/bookings **Hapana Business API v2** **Base URL**: `https://public-api.hapana-app.com/public-api/v2` ## Summary Book a client into a session ## Description Books any client at the site, as the front desk would. Refusals carry a code: SESSION_FULL, NO_VALID_PACKAGE, ALREADY_BOOKED, BOOKING_OVERLAP, CLIENT_BLOCKED. No unpaid booking is ever created. Send an Idempotency-Key — a replay returns the original booking. **Required scope:** `bookings:write` **Tags**: Bookings **Authentication**: ```json [ { "partnerKey": [] } ] ``` ## Parameters | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | Idempotency-Key | header | string | no | Makes the request safe to retry. A retry with the same key returns the first response; reusing a key with a different body is refused with 422. | ## Request Body ```json { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "siteId": { "type": "string", "format": "uuid", "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$" }, "clientId": { "type": "string", "format": "uuid", "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$" }, "sessionId": { "type": "string", "minLength": 1 }, "spotId": { "type": "integer", "minimum": 0, "maximum": 9007199254740991 }, "notes": { "type": "string", "maxLength": 1000 } }, "required": [ "siteId", "clientId", "sessionId" ] } } } } ``` ## Responses ### 201 — Success ```json { "description": "Success", "content": { "application/json": { "schema": { "type": "object", "properties": { "object": { "type": "string", "const": "booking" }, "id": { "type": "string" }, "siteId": { "type": "string" }, "clientId": { "type": "string" }, "status": { "type": "string", "enum": [ "PENDING", "CONFIRMED", "DECLINED", "CANCELLED", "LATE_CANCELLED", "ATTENDED", "NO_SHOW", "INVITED", "EXPIRED", "REJECTED" ] }, "cancelled": { "type": "boolean" }, "paymentSource": { "anyOf": [ { "type": "string", "enum": [ "CREDIT", "FREE", "DROP_IN", "BILLING", "ADMIN", "AGGREGATOR", "IMPORT", "GUEST_PASS" ] }, { "type": "null" } ] }, "paymentStatus": { "anyOf": [ { "type": "string", "enum": [ "paid", "pending" ] }, { "type": "null" } ] }, "spotId": { "anyOf": [ { "type": "integer", "minimum": -9007199254740991, "maximum": 9007199254740991 }, { "type": "null" } ] }, "createdAt": { "type": "string", "format": "date-time", "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$" }, "cancelledAt": { "anyOf": [ { "type": "string", "format": "date-time", "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$" }, { "type": "null" } ] }, "session": { "type": "object", "properties": { "id": { "type": "string" }, "name": { "type": "string" }, "category": { "type": "string", "enum": [ "CLASS", "PRIVATE", "WORKSHOP" ] }, "status": { "type": "string", "enum": [ "SCHEDULED", "IN_PROGRESS", "COMPLETED", "CANCELLED" ] }, "startTime": { "type": "string", "format": "date-time", "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$" }, "endTime": { "type": "string", "format": "date-time", "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$" }, "timezone": { "anyOf": [ { "type": "string" }, { "type": "null" } ] }, "localStartTime": { "anyOf": [ { "type": "string" }, { "type": "null" } ] }, "localEndTime": { "anyOf": [ { "type": "string" }, { "type": "null" } ] } }, "required": [ "id", "name", "category", "status", "startTime", "endTime", "timezone", "localStartTime", "localEndTime" ] } }, "required": [ "object", "id", "siteId", "clientId", "status", "cancelled", "paymentSource", "paymentStatus", "spotId", "createdAt", "cancelledAt", "session" ] } } } } ``` ### 400 — Malformed request ```json { "description": "Malformed request", "content": { "application/json": { "schema": { "type": "object", "properties": { "error": { "type": "string" }, "code": { "type": "string" }, "message": { "type": "string" } }, "required": [ "error" ] } } } } ``` ### 403 — The API key does not have the required scope ```json { "description": "The API key does not have the required scope", "content": { "application/json": { "schema": { "type": "object", "properties": { "error": { "type": "string" }, "code": { "type": "string" }, "message": { "type": "string" } }, "required": [ "error" ] } } } } ``` ### 404 — Not found. A record that belongs to another brand also returns 404. ```json { "description": "Not found. A record that belongs to another brand also returns 404.", "content": { "application/json": { "schema": { "type": "object", "properties": { "error": { "type": "string" }, "code": { "type": "string" }, "message": { "type": "string" } }, "required": [ "error" ] } } } } ``` ### 409 — A request with this Idempotency-Key is still being processed ```json { "description": "A request with this Idempotency-Key is still being processed" } ``` ### 422 — Idempotency-Key reused with a different request body ```json { "description": "Idempotency-Key reused with a different request body" } ``` ## Schemas in scope Refer to the full OpenAPI spec for resolved `$ref` definitions: - https://docs.hapana-developers.com/openapi/business-v2.json --- Source spec: `docs/openapi/hapana-business-api.openapi.json` · operationId `post_bookings` · version `v2` ## Security schemes ```json { "partnerKey": { "type": "http", "scheme": "bearer", "description": "The brand API key, e.g. `hk_test_…`. Paste the value only, without `Bearer`." } } ``` # GET /public-api/v2/bookings/{id} **Hapana Business API v2** **Base URL**: `https://public-api.hapana-app.com/public-api/v2` ## Summary Retrieve a booking ## Description **Required scope:** `bookings:read` **Tags**: Bookings **Authentication**: ```json [ { "partnerKey": [] } ] ``` ## Parameters | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | id | path | string | yes | | | siteId | query | string | yes | | ## Responses ### 200 — Success ```json { "description": "Success", "content": { "application/json": { "schema": { "type": "object", "properties": { "object": { "type": "string", "const": "booking" }, "id": { "type": "string" }, "siteId": { "type": "string" }, "clientId": { "type": "string" }, "status": { "type": "string", "enum": [ "PENDING", "CONFIRMED", "DECLINED", "CANCELLED", "LATE_CANCELLED", "ATTENDED", "NO_SHOW", "INVITED", "EXPIRED", "REJECTED" ] }, "cancelled": { "type": "boolean" }, "paymentSource": { "anyOf": [ { "type": "string", "enum": [ "CREDIT", "FREE", "DROP_IN", "BILLING", "ADMIN", "AGGREGATOR", "IMPORT", "GUEST_PASS" ] }, { "type": "null" } ] }, "paymentStatus": { "anyOf": [ { "type": "string", "enum": [ "paid", "pending" ] }, { "type": "null" } ] }, "spotId": { "anyOf": [ { "type": "integer", "minimum": -9007199254740991, "maximum": 9007199254740991 }, { "type": "null" } ] }, "createdAt": { "type": "string", "format": "date-time", "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$" }, "cancelledAt": { "anyOf": [ { "type": "string", "format": "date-time", "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$" }, { "type": "null" } ] }, "session": { "type": "object", "properties": { "id": { "type": "string" }, "name": { "type": "string" }, "category": { "type": "string", "enum": [ "CLASS", "PRIVATE", "WORKSHOP" ] }, "status": { "type": "string", "enum": [ "SCHEDULED", "IN_PROGRESS", "COMPLETED", "CANCELLED" ] }, "startTime": { "type": "string", "format": "date-time", "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$" }, "endTime": { "type": "string", "format": "date-time", "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$" }, "timezone": { "anyOf": [ { "type": "string" }, { "type": "null" } ] }, "localStartTime": { "anyOf": [ { "type": "string" }, { "type": "null" } ] }, "localEndTime": { "anyOf": [ { "type": "string" }, { "type": "null" } ] } }, "required": [ "id", "name", "category", "status", "startTime", "endTime", "timezone", "localStartTime", "localEndTime" ] } }, "required": [ "object", "id", "siteId", "clientId", "status", "cancelled", "paymentSource", "paymentStatus", "spotId", "createdAt", "cancelledAt", "session" ] } } } } ``` ### 400 — Malformed request ```json { "description": "Malformed request", "content": { "application/json": { "schema": { "type": "object", "properties": { "error": { "type": "string" }, "code": { "type": "string" }, "message": { "type": "string" } }, "required": [ "error" ] } } } } ``` ### 403 — The API key does not have the required scope ```json { "description": "The API key does not have the required scope", "content": { "application/json": { "schema": { "type": "object", "properties": { "error": { "type": "string" }, "code": { "type": "string" }, "message": { "type": "string" } }, "required": [ "error" ] } } } } ``` ### 404 — Not found. A record that belongs to another brand also returns 404. ```json { "description": "Not found. A record that belongs to another brand also returns 404.", "content": { "application/json": { "schema": { "type": "object", "properties": { "error": { "type": "string" }, "code": { "type": "string" }, "message": { "type": "string" } }, "required": [ "error" ] } } } } ``` ## Schemas in scope Refer to the full OpenAPI spec for resolved `$ref` definitions: - https://docs.hapana-developers.com/openapi/business-v2.json --- Source spec: `docs/openapi/hapana-business-api.openapi.json` · operationId `get_bookings__id_` · version `v2` ## Security schemes ```json { "partnerKey": { "type": "http", "scheme": "bearer", "description": "The brand API key, e.g. `hk_test_…`. Paste the value only, without `Bearer`." } } ``` # GET /public-api/v2/bookings/{id}/cancellation **Hapana Business API v2** **Base URL**: `https://public-api.hapana-app.com/public-api/v2` ## Summary Preview what cancelling would cost ## Description Whether a cancellation now is late, until when it is free, the credits that would be forfeited and any late-cancel fee. Nothing is changed. **Required scope:** `bookings:read` **Tags**: Bookings **Authentication**: ```json [ { "partnerKey": [] } ] ``` ## Parameters | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | id | path | string | yes | | | siteId | query | string | yes | | ## Responses ### 200 — Success ```json { "description": "Success", "content": { "application/json": { "schema": { "type": "object", "properties": { "object": { "type": "string", "const": "cancellation_preview" }, "bookingId": { "type": "string" }, "lateCancel": { "type": "boolean" }, "cancellableUntil": { "anyOf": [ { "type": "string", "format": "date-time", "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$" }, { "type": "null" } ] }, "creditsForfeited": { "type": "integer", "minimum": -9007199254740991, "maximum": 9007199254740991 }, "lateCancelFee": { "anyOf": [ { "type": "string" }, { "type": "null" } ] } }, "required": [ "object", "bookingId", "lateCancel", "cancellableUntil", "creditsForfeited", "lateCancelFee" ] } } } } ``` ### 400 — Malformed request ```json { "description": "Malformed request", "content": { "application/json": { "schema": { "type": "object", "properties": { "error": { "type": "string" }, "code": { "type": "string" }, "message": { "type": "string" } }, "required": [ "error" ] } } } } ``` ### 403 — The API key does not have the required scope ```json { "description": "The API key does not have the required scope", "content": { "application/json": { "schema": { "type": "object", "properties": { "error": { "type": "string" }, "code": { "type": "string" }, "message": { "type": "string" } }, "required": [ "error" ] } } } } ``` ### 404 — Not found. A record that belongs to another brand also returns 404. ```json { "description": "Not found. A record that belongs to another brand also returns 404.", "content": { "application/json": { "schema": { "type": "object", "properties": { "error": { "type": "string" }, "code": { "type": "string" }, "message": { "type": "string" } }, "required": [ "error" ] } } } } ``` ## Schemas in scope Refer to the full OpenAPI spec for resolved `$ref` definitions: - https://docs.hapana-developers.com/openapi/business-v2.json --- Source spec: `docs/openapi/hapana-business-api.openapi.json` · operationId `get_bookings__id__cancellation` · version `v2` ## Security schemes ```json { "partnerKey": { "type": "http", "scheme": "bearer", "description": "The brand API key, e.g. `hk_test_…`. Paste the value only, without `Bearer`." } } ``` # POST /public-api/v2/bookings/{id}/cancel **Hapana Business API v2** **Base URL**: `https://public-api.hapana-app.com/public-api/v2` ## Summary Cancel a booking ## Description Inside the late-cancel window the booking becomes LATE_CANCELLED and the site policy applies. A paid drop-in is refunded to account credit, never to the card. **Required scope:** `bookings:write` **Tags**: Bookings **Authentication**: ```json [ { "partnerKey": [] } ] ``` ## Parameters | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | id | path | string | yes | | | Idempotency-Key | header | string | no | Makes the request safe to retry. A retry with the same key returns the first response; reusing a key with a different body is refused with 422. | ## Request Body ```json { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "siteId": { "type": "string", "format": "uuid", "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$" } }, "required": [ "siteId" ] } } } } ``` ## Responses ### 200 — Success ```json { "description": "Success", "content": { "application/json": { "schema": { "type": "object", "properties": { "object": { "type": "string", "const": "booking" }, "id": { "type": "string" }, "siteId": { "type": "string" }, "clientId": { "type": "string" }, "status": { "type": "string", "enum": [ "PENDING", "CONFIRMED", "DECLINED", "CANCELLED", "LATE_CANCELLED", "ATTENDED", "NO_SHOW", "INVITED", "EXPIRED", "REJECTED" ] }, "cancelled": { "type": "boolean" }, "paymentSource": { "anyOf": [ { "type": "string", "enum": [ "CREDIT", "FREE", "DROP_IN", "BILLING", "ADMIN", "AGGREGATOR", "IMPORT", "GUEST_PASS" ] }, { "type": "null" } ] }, "paymentStatus": { "anyOf": [ { "type": "string", "enum": [ "paid", "pending" ] }, { "type": "null" } ] }, "spotId": { "anyOf": [ { "type": "integer", "minimum": -9007199254740991, "maximum": 9007199254740991 }, { "type": "null" } ] }, "createdAt": { "type": "string", "format": "date-time", "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$" }, "cancelledAt": { "anyOf": [ { "type": "string", "format": "date-time", "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$" }, { "type": "null" } ] }, "session": { "type": "object", "properties": { "id": { "type": "string" }, "name": { "type": "string" }, "category": { "type": "string", "enum": [ "CLASS", "PRIVATE", "WORKSHOP" ] }, "status": { "type": "string", "enum": [ "SCHEDULED", "IN_PROGRESS", "COMPLETED", "CANCELLED" ] }, "startTime": { "type": "string", "format": "date-time", "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$" }, "endTime": { "type": "string", "format": "date-time", "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$" }, "timezone": { "anyOf": [ { "type": "string" }, { "type": "null" } ] }, "localStartTime": { "anyOf": [ { "type": "string" }, { "type": "null" } ] }, "localEndTime": { "anyOf": [ { "type": "string" }, { "type": "null" } ] } }, "required": [ "id", "name", "category", "status", "startTime", "endTime", "timezone", "localStartTime", "localEndTime" ] } }, "required": [ "object", "id", "siteId", "clientId", "status", "cancelled", "paymentSource", "paymentStatus", "spotId", "createdAt", "cancelledAt", "session" ] } } } } ``` ### 400 — Malformed request ```json { "description": "Malformed request", "content": { "application/json": { "schema": { "type": "object", "properties": { "error": { "type": "string" }, "code": { "type": "string" }, "message": { "type": "string" } }, "required": [ "error" ] } } } } ``` ### 403 — The API key does not have the required scope ```json { "description": "The API key does not have the required scope", "content": { "application/json": { "schema": { "type": "object", "properties": { "error": { "type": "string" }, "code": { "type": "string" }, "message": { "type": "string" } }, "required": [ "error" ] } } } } ``` ### 404 — Not found. A record that belongs to another brand also returns 404. ```json { "description": "Not found. A record that belongs to another brand also returns 404.", "content": { "application/json": { "schema": { "type": "object", "properties": { "error": { "type": "string" }, "code": { "type": "string" }, "message": { "type": "string" } }, "required": [ "error" ] } } } } ``` ### 409 — A request with this Idempotency-Key is still being processed ```json { "description": "A request with this Idempotency-Key is still being processed" } ``` ### 422 — Idempotency-Key reused with a different request body ```json { "description": "Idempotency-Key reused with a different request body" } ``` ## Schemas in scope Refer to the full OpenAPI spec for resolved `$ref` definitions: - https://docs.hapana-developers.com/openapi/business-v2.json --- Source spec: `docs/openapi/hapana-business-api.openapi.json` · operationId `post_bookings__id__cancel` · version `v2` ## Security schemes ```json { "partnerKey": { "type": "http", "scheme": "bearer", "description": "The brand API key, e.g. `hk_test_…`. Paste the value only, without `Bearer`." } } ``` # POST /public-api/v2/sessions/{id}/waitlist **Hapana Business API v2** **Base URL**: `https://public-api.hapana-app.com/public-api/v2` ## Summary Join a session's waitlist ## Description Refused with WAITLIST_NOT_ENABLED, WAITLIST_FULL, WAITLIST_LOCKED, ALREADY_ON_WAITLIST, ALREADY_BOOKED or SPOT_AVAILABLE (waitlist strategy ALL with a spot open: book it instead). Accepts any session id GET /sessions returns. **Required scope:** `bookings:write` **Tags**: Bookings **Authentication**: ```json [ { "partnerKey": [] } ] ``` ## Parameters | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | id | path | string | yes | | | Idempotency-Key | header | string | no | Makes the request safe to retry. A retry with the same key returns the first response; reusing a key with a different body is refused with 422. | ## Request Body ```json { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "siteId": { "type": "string", "format": "uuid", "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$" }, "clientId": { "type": "string", "format": "uuid", "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$" } }, "required": [ "siteId", "clientId" ] } } } } ``` ## Responses ### 201 — Success ```json { "description": "Success", "content": { "application/json": { "schema": { "type": "object", "properties": { "object": { "type": "string", "const": "waitlist_entry" }, "id": { "type": "string" }, "sessionId": { "type": "string" }, "clientId": { "type": "string" }, "position": { "type": "integer", "minimum": -9007199254740991, "maximum": 9007199254740991 }, "status": { "type": "string", "enum": [ "WAITING", "INVITED", "CONVERTED", "EXPIRED", "CANCELLED" ] } }, "required": [ "object", "id", "sessionId", "clientId", "position", "status" ] } } } } ``` ### 400 — Malformed request ```json { "description": "Malformed request", "content": { "application/json": { "schema": { "type": "object", "properties": { "error": { "type": "string" }, "code": { "type": "string" }, "message": { "type": "string" } }, "required": [ "error" ] } } } } ``` ### 403 — The API key does not have the required scope ```json { "description": "The API key does not have the required scope", "content": { "application/json": { "schema": { "type": "object", "properties": { "error": { "type": "string" }, "code": { "type": "string" }, "message": { "type": "string" } }, "required": [ "error" ] } } } } ``` ### 404 — Not found. A record that belongs to another brand also returns 404. ```json { "description": "Not found. A record that belongs to another brand also returns 404.", "content": { "application/json": { "schema": { "type": "object", "properties": { "error": { "type": "string" }, "code": { "type": "string" }, "message": { "type": "string" } }, "required": [ "error" ] } } } } ``` ### 409 — A request with this Idempotency-Key is still being processed ```json { "description": "A request with this Idempotency-Key is still being processed" } ``` ### 422 — Idempotency-Key reused with a different request body ```json { "description": "Idempotency-Key reused with a different request body" } ``` ## Schemas in scope Refer to the full OpenAPI spec for resolved `$ref` definitions: - https://docs.hapana-developers.com/openapi/business-v2.json --- Source spec: `docs/openapi/hapana-business-api.openapi.json` · operationId `post_sessions__id__waitlist` · version `v2` ## Security schemes ```json { "partnerKey": { "type": "http", "scheme": "bearer", "description": "The brand API key, e.g. `hk_test_…`. Paste the value only, without `Bearer`." } } ``` # POST /public-api/v2/sessions/{id}/waitlist/{clientId}/leave **Hapana Business API v2** **Base URL**: `https://public-api.hapana-app.com/public-api/v2` ## Summary Leave a waitlist ## Description Answers 204 whether or not the client was on it, so a retry is safe. **Required scope:** `bookings:write` **Tags**: Bookings **Authentication**: ```json [ { "partnerKey": [] } ] ``` ## Parameters | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | id | path | string | yes | | | clientId | path | string | yes | | | siteId | query | string | yes | | | Idempotency-Key | header | string | no | Makes the request safe to retry. A retry with the same key returns the first response; reusing a key with a different body is refused with 422. | ## Responses ### 204 — No content ```json { "description": "No content" } ``` ### 400 — Malformed request ```json { "description": "Malformed request", "content": { "application/json": { "schema": { "type": "object", "properties": { "error": { "type": "string" }, "code": { "type": "string" }, "message": { "type": "string" } }, "required": [ "error" ] } } } } ``` ### 403 — The API key does not have the required scope ```json { "description": "The API key does not have the required scope", "content": { "application/json": { "schema": { "type": "object", "properties": { "error": { "type": "string" }, "code": { "type": "string" }, "message": { "type": "string" } }, "required": [ "error" ] } } } } ``` ### 404 — Not found. A record that belongs to another brand also returns 404. ```json { "description": "Not found. A record that belongs to another brand also returns 404.", "content": { "application/json": { "schema": { "type": "object", "properties": { "error": { "type": "string" }, "code": { "type": "string" }, "message": { "type": "string" } }, "required": [ "error" ] } } } } ``` ### 409 — A request with this Idempotency-Key is still being processed ```json { "description": "A request with this Idempotency-Key is still being processed" } ``` ### 422 — Idempotency-Key reused with a different request body ```json { "description": "Idempotency-Key reused with a different request body" } ``` ## Schemas in scope Refer to the full OpenAPI spec for resolved `$ref` definitions: - https://docs.hapana-developers.com/openapi/business-v2.json --- Source spec: `docs/openapi/hapana-business-api.openapi.json` · operationId `post_sessions__id__waitlist__clientId__leave` · version `v2` ## Security schemes ```json { "partnerKey": { "type": "http", "scheme": "bearer", "description": "The brand API key, e.g. `hk_test_…`. Paste the value only, without `Bearer`." } } ``` # POST /public-api/v2/bookings/{id}/check-in **Hapana Business API v2** **Base URL**: `https://public-api.hapana-app.com/public-api/v2` ## Summary Mark a booking as attended ## Description Records attendance after the class has run, exactly as the front desk would, so visit history and credit consumption stay consistent. **Required scope:** `bookings:write` **Tags**: Bookings **Authentication**: ```json [ { "partnerKey": [] } ] ``` ## Parameters | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | id | path | string | yes | | | Idempotency-Key | header | string | no | Makes the request safe to retry. A retry with the same key returns the first response; reusing a key with a different body is refused with 422. | ## Request Body ```json { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "siteId": { "type": "string", "format": "uuid", "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$" } }, "required": [ "siteId" ] } } } } ``` ## Responses ### 200 — Success ```json { "description": "Success", "content": { "application/json": { "schema": { "type": "object", "properties": { "object": { "type": "string", "const": "booking" }, "id": { "type": "string" }, "siteId": { "type": "string" }, "clientId": { "type": "string" }, "status": { "type": "string", "enum": [ "PENDING", "CONFIRMED", "DECLINED", "CANCELLED", "LATE_CANCELLED", "ATTENDED", "NO_SHOW", "INVITED", "EXPIRED", "REJECTED" ] }, "cancelled": { "type": "boolean" }, "paymentSource": { "anyOf": [ { "type": "string", "enum": [ "CREDIT", "FREE", "DROP_IN", "BILLING", "ADMIN", "AGGREGATOR", "IMPORT", "GUEST_PASS" ] }, { "type": "null" } ] }, "paymentStatus": { "anyOf": [ { "type": "string", "enum": [ "paid", "pending" ] }, { "type": "null" } ] }, "spotId": { "anyOf": [ { "type": "integer", "minimum": -9007199254740991, "maximum": 9007199254740991 }, { "type": "null" } ] }, "createdAt": { "type": "string", "format": "date-time", "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$" }, "cancelledAt": { "anyOf": [ { "type": "string", "format": "date-time", "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$" }, { "type": "null" } ] }, "session": { "type": "object", "properties": { "id": { "type": "string" }, "name": { "type": "string" }, "category": { "type": "string", "enum": [ "CLASS", "PRIVATE", "WORKSHOP" ] }, "status": { "type": "string", "enum": [ "SCHEDULED", "IN_PROGRESS", "COMPLETED", "CANCELLED" ] }, "startTime": { "type": "string", "format": "date-time", "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$" }, "endTime": { "type": "string", "format": "date-time", "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$" }, "timezone": { "anyOf": [ { "type": "string" }, { "type": "null" } ] }, "localStartTime": { "anyOf": [ { "type": "string" }, { "type": "null" } ] }, "localEndTime": { "anyOf": [ { "type": "string" }, { "type": "null" } ] } }, "required": [ "id", "name", "category", "status", "startTime", "endTime", "timezone", "localStartTime", "localEndTime" ] } }, "required": [ "object", "id", "siteId", "clientId", "status", "cancelled", "paymentSource", "paymentStatus", "spotId", "createdAt", "cancelledAt", "session" ] } } } } ``` ### 400 — Malformed request ```json { "description": "Malformed request", "content": { "application/json": { "schema": { "type": "object", "properties": { "error": { "type": "string" }, "code": { "type": "string" }, "message": { "type": "string" } }, "required": [ "error" ] } } } } ``` ### 403 — The API key does not have the required scope ```json { "description": "The API key does not have the required scope", "content": { "application/json": { "schema": { "type": "object", "properties": { "error": { "type": "string" }, "code": { "type": "string" }, "message": { "type": "string" } }, "required": [ "error" ] } } } } ``` ### 404 — Not found. A record that belongs to another brand also returns 404. ```json { "description": "Not found. A record that belongs to another brand also returns 404.", "content": { "application/json": { "schema": { "type": "object", "properties": { "error": { "type": "string" }, "code": { "type": "string" }, "message": { "type": "string" } }, "required": [ "error" ] } } } } ``` ### 409 — A request with this Idempotency-Key is still being processed ```json { "description": "A request with this Idempotency-Key is still being processed" } ``` ### 422 — Idempotency-Key reused with a different request body ```json { "description": "Idempotency-Key reused with a different request body" } ``` ## Schemas in scope Refer to the full OpenAPI spec for resolved `$ref` definitions: - https://docs.hapana-developers.com/openapi/business-v2.json --- Source spec: `docs/openapi/hapana-business-api.openapi.json` · operationId `post_bookings__id__check_in` · version `v2` ## Security schemes ```json { "partnerKey": { "type": "http", "scheme": "bearer", "description": "The brand API key, e.g. `hk_test_…`. Paste the value only, without `Bearer`." } } ``` # POST /public-api/v2/bookings/{id}/no-show **Hapana Business API v2** **Base URL**: `https://public-api.hapana-app.com/public-api/v2` ## Summary Mark a booking as a no-show ## Description A no-show is a retention signal, not a dead end: the site may charge its configured no-show fee, and the booking stays on the member’s history. **Required scope:** `bookings:write` **Tags**: Bookings **Authentication**: ```json [ { "partnerKey": [] } ] ``` ## Parameters | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | id | path | string | yes | | | Idempotency-Key | header | string | no | Makes the request safe to retry. A retry with the same key returns the first response; reusing a key with a different body is refused with 422. | ## Request Body ```json { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "siteId": { "type": "string", "format": "uuid", "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$" } }, "required": [ "siteId" ] } } } } ``` ## Responses ### 200 — Success ```json { "description": "Success", "content": { "application/json": { "schema": { "type": "object", "properties": { "object": { "type": "string", "const": "booking" }, "id": { "type": "string" }, "siteId": { "type": "string" }, "clientId": { "type": "string" }, "status": { "type": "string", "enum": [ "PENDING", "CONFIRMED", "DECLINED", "CANCELLED", "LATE_CANCELLED", "ATTENDED", "NO_SHOW", "INVITED", "EXPIRED", "REJECTED" ] }, "cancelled": { "type": "boolean" }, "paymentSource": { "anyOf": [ { "type": "string", "enum": [ "CREDIT", "FREE", "DROP_IN", "BILLING", "ADMIN", "AGGREGATOR", "IMPORT", "GUEST_PASS" ] }, { "type": "null" } ] }, "paymentStatus": { "anyOf": [ { "type": "string", "enum": [ "paid", "pending" ] }, { "type": "null" } ] }, "spotId": { "anyOf": [ { "type": "integer", "minimum": -9007199254740991, "maximum": 9007199254740991 }, { "type": "null" } ] }, "createdAt": { "type": "string", "format": "date-time", "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$" }, "cancelledAt": { "anyOf": [ { "type": "string", "format": "date-time", "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$" }, { "type": "null" } ] }, "session": { "type": "object", "properties": { "id": { "type": "string" }, "name": { "type": "string" }, "category": { "type": "string", "enum": [ "CLASS", "PRIVATE", "WORKSHOP" ] }, "status": { "type": "string", "enum": [ "SCHEDULED", "IN_PROGRESS", "COMPLETED", "CANCELLED" ] }, "startTime": { "type": "string", "format": "date-time", "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$" }, "endTime": { "type": "string", "format": "date-time", "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$" }, "timezone": { "anyOf": [ { "type": "string" }, { "type": "null" } ] }, "localStartTime": { "anyOf": [ { "type": "string" }, { "type": "null" } ] }, "localEndTime": { "anyOf": [ { "type": "string" }, { "type": "null" } ] } }, "required": [ "id", "name", "category", "status", "startTime", "endTime", "timezone", "localStartTime", "localEndTime" ] } }, "required": [ "object", "id", "siteId", "clientId", "status", "cancelled", "paymentSource", "paymentStatus", "spotId", "createdAt", "cancelledAt", "session" ] } } } } ``` ### 400 — Malformed request ```json { "description": "Malformed request", "content": { "application/json": { "schema": { "type": "object", "properties": { "error": { "type": "string" }, "code": { "type": "string" }, "message": { "type": "string" } }, "required": [ "error" ] } } } } ``` ### 403 — The API key does not have the required scope ```json { "description": "The API key does not have the required scope", "content": { "application/json": { "schema": { "type": "object", "properties": { "error": { "type": "string" }, "code": { "type": "string" }, "message": { "type": "string" } }, "required": [ "error" ] } } } } ``` ### 404 — Not found. A record that belongs to another brand also returns 404. ```json { "description": "Not found. A record that belongs to another brand also returns 404.", "content": { "application/json": { "schema": { "type": "object", "properties": { "error": { "type": "string" }, "code": { "type": "string" }, "message": { "type": "string" } }, "required": [ "error" ] } } } } ``` ### 409 — A request with this Idempotency-Key is still being processed ```json { "description": "A request with this Idempotency-Key is still being processed" } ``` ### 422 — Idempotency-Key reused with a different request body ```json { "description": "Idempotency-Key reused with a different request body" } ``` ## Schemas in scope Refer to the full OpenAPI spec for resolved `$ref` definitions: - https://docs.hapana-developers.com/openapi/business-v2.json --- Source spec: `docs/openapi/hapana-business-api.openapi.json` · operationId `post_bookings__id__no_show` · version `v2` ## Security schemes ```json { "partnerKey": { "type": "http", "scheme": "bearer", "description": "The brand API key, e.g. `hk_test_…`. Paste the value only, without `Bearer`." } } ``` # GET /public-api/v2/checkins **Hapana Business API v2** **Base URL**: `https://public-api.hapana-app.com/public-api/v2` ## Summary List check-ins ## Description The visit feed for a site. A refused entry is recorded too, with status FAIL and the reason, which is what makes the feed usable for support. **Required scope:** `checkins:read` **Tags**: Check-ins **Authentication**: ```json [ { "partnerKey": [] } ] ``` ## Parameters | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | cursor | query | string | no | | | limit | query | integer | no | | | siteId | query | string | yes | | | clientId | query | string | no | | | startDate | query | string | no | | | endDate | query | string | no | | | type | query | string | no | | | status | query | string | no | | ## Responses ### 200 — Success ```json { "description": "Success", "content": { "application/json": { "schema": { "type": "object", "properties": { "object": { "type": "string", "enum": [ "list" ] }, "data": { "type": "array", "items": { "type": "object", "properties": { "object": { "type": "string", "const": "checkin" }, "id": { "type": "string" }, "siteId": { "type": "string" }, "clientId": { "type": "string" }, "type": { "type": "string", "enum": [ "SESSION", "GENERAL", "TANNING", "ZONE" ] }, "status": { "type": "string", "enum": [ "SUCCESS", "FAIL", "UNKNOWN" ] }, "source": { "type": "string", "enum": [ "BARCODE_SCAN", "BARCODE_DEVICE_SCAN", "FOB_DEVICE_SCAN", "CONTACTLESS", "FOB_SCAN", "IN_APP", "KIOSK", "PUBLIC_API", "QR_CODE", "WIDGET", "CORE", "IMPORT", "AGGREGATOR" ] }, "failReason": { "anyOf": [ { "type": "string", "enum": [ "FAILED_PAYMENTS", "NO_VALID_MEMBERSHIP_OR_BOOKING", "BOOKING_PAYMENT_PENDING", "SUSPENDED", "NO_ACTIVE_PACKAGE", "MEMBERSHIP_ON_HOLD", "NO_CREDITS", "NOT_IN_TIME", "QUESTIONNAIRE_FAILED", "CHECK_IN_WINDOW_EXPIRED", "AGGREGATOR_DECLINED", "AGGREGATOR_VALIDATION_REQUIRED" ] }, { "type": "null" } ] }, "sessionId": { "anyOf": [ { "type": "string" }, { "type": "null" } ] }, "bookingId": { "anyOf": [ { "type": "string" }, { "type": "null" } ] }, "checkedInAt": { "type": "string", "format": "date-time", "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$" }, "timezone": { "anyOf": [ { "type": "string" }, { "type": "null" } ] }, "localCheckedInAt": { "anyOf": [ { "type": "string" }, { "type": "null" } ] } }, "required": [ "object", "id", "siteId", "clientId", "type", "status", "source", "failReason", "sessionId", "bookingId", "checkedInAt", "timezone", "localCheckedInAt" ] } }, "hasMore": { "type": "boolean" }, "nextCursor": { "type": [ "string", "null" ] }, "url": { "type": "string" } }, "required": [ "object", "data", "hasMore", "url" ] } } } } ``` ### 400 — Malformed request ```json { "description": "Malformed request", "content": { "application/json": { "schema": { "type": "object", "properties": { "error": { "type": "string" }, "code": { "type": "string" }, "message": { "type": "string" } }, "required": [ "error" ] } } } } ``` ### 403 — The API key does not have the required scope ```json { "description": "The API key does not have the required scope", "content": { "application/json": { "schema": { "type": "object", "properties": { "error": { "type": "string" }, "code": { "type": "string" }, "message": { "type": "string" } }, "required": [ "error" ] } } } } ``` ### 404 — Not found. A record that belongs to another brand also returns 404. ```json { "description": "Not found. A record that belongs to another brand also returns 404.", "content": { "application/json": { "schema": { "type": "object", "properties": { "error": { "type": "string" }, "code": { "type": "string" }, "message": { "type": "string" } }, "required": [ "error" ] } } } } ``` ## Schemas in scope Refer to the full OpenAPI spec for resolved `$ref` definitions: - https://docs.hapana-developers.com/openapi/business-v2.json --- Source spec: `docs/openapi/hapana-business-api.openapi.json` · operationId `get_checkins` · version `v2` ## Security schemes ```json { "partnerKey": { "type": "http", "scheme": "bearer", "description": "The brand API key, e.g. `hk_test_…`. Paste the value only, without `Bearer`." } } ``` # POST /public-api/v2/checkins **Hapana Business API v2** **Base URL**: `https://public-api.hapana-app.com/public-api/v2` ## Summary Record a walk-in check-in ## Description For a visit with no booking. A member who may not enter is recorded with status FAIL and a failReason rather than the request failing. Check a booked class in through POST /bookings/{id}/check-in instead, so the class roster stays in step. **Required scope:** `checkins:write` **Tags**: Check-ins **Authentication**: ```json [ { "partnerKey": [] } ] ``` ## Parameters | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | Idempotency-Key | header | string | no | Makes the request safe to retry. A retry with the same key returns the first response; reusing a key with a different body is refused with 422. | ## Request Body ```json { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "siteId": { "type": "string", "format": "uuid", "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$" }, "clientId": { "type": "string", "format": "uuid", "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$" } }, "required": [ "siteId", "clientId" ] } } } } ``` ## Responses ### 201 — Success ```json { "description": "Success", "content": { "application/json": { "schema": { "type": "object", "properties": { "object": { "type": "string", "const": "checkin" }, "id": { "type": "string" }, "siteId": { "type": "string" }, "clientId": { "type": "string" }, "type": { "type": "string", "enum": [ "SESSION", "GENERAL", "TANNING", "ZONE" ] }, "status": { "type": "string", "enum": [ "SUCCESS", "FAIL", "UNKNOWN" ] }, "source": { "type": "string", "enum": [ "BARCODE_SCAN", "BARCODE_DEVICE_SCAN", "FOB_DEVICE_SCAN", "CONTACTLESS", "FOB_SCAN", "IN_APP", "KIOSK", "PUBLIC_API", "QR_CODE", "WIDGET", "CORE", "IMPORT", "AGGREGATOR" ] }, "failReason": { "anyOf": [ { "type": "string", "enum": [ "FAILED_PAYMENTS", "NO_VALID_MEMBERSHIP_OR_BOOKING", "BOOKING_PAYMENT_PENDING", "SUSPENDED", "NO_ACTIVE_PACKAGE", "MEMBERSHIP_ON_HOLD", "NO_CREDITS", "NOT_IN_TIME", "QUESTIONNAIRE_FAILED", "CHECK_IN_WINDOW_EXPIRED", "AGGREGATOR_DECLINED", "AGGREGATOR_VALIDATION_REQUIRED" ] }, { "type": "null" } ] }, "sessionId": { "anyOf": [ { "type": "string" }, { "type": "null" } ] }, "bookingId": { "anyOf": [ { "type": "string" }, { "type": "null" } ] }, "checkedInAt": { "type": "string", "format": "date-time", "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$" }, "timezone": { "anyOf": [ { "type": "string" }, { "type": "null" } ] }, "localCheckedInAt": { "anyOf": [ { "type": "string" }, { "type": "null" } ] } }, "required": [ "object", "id", "siteId", "clientId", "type", "status", "source", "failReason", "sessionId", "bookingId", "checkedInAt", "timezone", "localCheckedInAt" ] } } } } ``` ### 400 — Malformed request ```json { "description": "Malformed request", "content": { "application/json": { "schema": { "type": "object", "properties": { "error": { "type": "string" }, "code": { "type": "string" }, "message": { "type": "string" } }, "required": [ "error" ] } } } } ``` ### 403 — The API key does not have the required scope ```json { "description": "The API key does not have the required scope", "content": { "application/json": { "schema": { "type": "object", "properties": { "error": { "type": "string" }, "code": { "type": "string" }, "message": { "type": "string" } }, "required": [ "error" ] } } } } ``` ### 404 — Not found. A record that belongs to another brand also returns 404. ```json { "description": "Not found. A record that belongs to another brand also returns 404.", "content": { "application/json": { "schema": { "type": "object", "properties": { "error": { "type": "string" }, "code": { "type": "string" }, "message": { "type": "string" } }, "required": [ "error" ] } } } } ``` ### 409 — A request with this Idempotency-Key is still being processed ```json { "description": "A request with this Idempotency-Key is still being processed" } ``` ### 422 — Idempotency-Key reused with a different request body ```json { "description": "Idempotency-Key reused with a different request body" } ``` ## Schemas in scope Refer to the full OpenAPI spec for resolved `$ref` definitions: - https://docs.hapana-developers.com/openapi/business-v2.json --- Source spec: `docs/openapi/hapana-business-api.openapi.json` · operationId `post_checkins` · version `v2` ## Security schemes ```json { "partnerKey": { "type": "http", "scheme": "bearer", "description": "The brand API key, e.g. `hk_test_…`. Paste the value only, without `Bearer`." } } ``` # GET /public-api/v2/checkins/{id} **Hapana Business API v2** **Base URL**: `https://public-api.hapana-app.com/public-api/v2` ## Summary Retrieve a check-in ## Description **Required scope:** `checkins:read` **Tags**: Check-ins **Authentication**: ```json [ { "partnerKey": [] } ] ``` ## Parameters | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | id | path | string | yes | | | siteId | query | string | yes | | ## Responses ### 200 — Success ```json { "description": "Success", "content": { "application/json": { "schema": { "type": "object", "properties": { "object": { "type": "string", "const": "checkin" }, "id": { "type": "string" }, "siteId": { "type": "string" }, "clientId": { "type": "string" }, "type": { "type": "string", "enum": [ "SESSION", "GENERAL", "TANNING", "ZONE" ] }, "status": { "type": "string", "enum": [ "SUCCESS", "FAIL", "UNKNOWN" ] }, "source": { "type": "string", "enum": [ "BARCODE_SCAN", "BARCODE_DEVICE_SCAN", "FOB_DEVICE_SCAN", "CONTACTLESS", "FOB_SCAN", "IN_APP", "KIOSK", "PUBLIC_API", "QR_CODE", "WIDGET", "CORE", "IMPORT", "AGGREGATOR" ] }, "failReason": { "anyOf": [ { "type": "string", "enum": [ "FAILED_PAYMENTS", "NO_VALID_MEMBERSHIP_OR_BOOKING", "BOOKING_PAYMENT_PENDING", "SUSPENDED", "NO_ACTIVE_PACKAGE", "MEMBERSHIP_ON_HOLD", "NO_CREDITS", "NOT_IN_TIME", "QUESTIONNAIRE_FAILED", "CHECK_IN_WINDOW_EXPIRED", "AGGREGATOR_DECLINED", "AGGREGATOR_VALIDATION_REQUIRED" ] }, { "type": "null" } ] }, "sessionId": { "anyOf": [ { "type": "string" }, { "type": "null" } ] }, "bookingId": { "anyOf": [ { "type": "string" }, { "type": "null" } ] }, "checkedInAt": { "type": "string", "format": "date-time", "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$" }, "timezone": { "anyOf": [ { "type": "string" }, { "type": "null" } ] }, "localCheckedInAt": { "anyOf": [ { "type": "string" }, { "type": "null" } ] } }, "required": [ "object", "id", "siteId", "clientId", "type", "status", "source", "failReason", "sessionId", "bookingId", "checkedInAt", "timezone", "localCheckedInAt" ] } } } } ``` ### 400 — Malformed request ```json { "description": "Malformed request", "content": { "application/json": { "schema": { "type": "object", "properties": { "error": { "type": "string" }, "code": { "type": "string" }, "message": { "type": "string" } }, "required": [ "error" ] } } } } ``` ### 403 — The API key does not have the required scope ```json { "description": "The API key does not have the required scope", "content": { "application/json": { "schema": { "type": "object", "properties": { "error": { "type": "string" }, "code": { "type": "string" }, "message": { "type": "string" } }, "required": [ "error" ] } } } } ``` ### 404 — Not found. A record that belongs to another brand also returns 404. ```json { "description": "Not found. A record that belongs to another brand also returns 404.", "content": { "application/json": { "schema": { "type": "object", "properties": { "error": { "type": "string" }, "code": { "type": "string" }, "message": { "type": "string" } }, "required": [ "error" ] } } } } ``` ## Schemas in scope Refer to the full OpenAPI spec for resolved `$ref` definitions: - https://docs.hapana-developers.com/openapi/business-v2.json --- Source spec: `docs/openapi/hapana-business-api.openapi.json` · operationId `get_checkins__id_` · version `v2` ## Security schemes ```json { "partnerKey": { "type": "http", "scheme": "bearer", "description": "The brand API key, e.g. `hk_test_…`. Paste the value only, without `Bearer`." } } ``` # GET /public-api/v2/clients/{id}/packages **Hapana Business API v2** **Base URL**: `https://public-api.hapana-app.com/public-api/v2` ## Summary List the packages a client owns ## Description What this member has bought, as distinct from what the site sells (GET /packages). Omit siteId to read every package the member holds across the brand in one call; each package names the site that owns it. accessSiteIds lists every site the package can be used at, home site included — the same rule booking and door access apply. **Required scope:** `packages:read` **Tags**: Client packages **Authentication**: ```json [ { "partnerKey": [] } ] ``` ## Parameters | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | id | path | string | yes | | | cursor | query | string | no | | | limit | query | integer | no | | | siteId | query | string | no | | | status | query | string | no | | ## Responses ### 200 — Success ```json { "description": "Success", "content": { "application/json": { "schema": { "type": "object", "properties": { "object": { "type": "string", "enum": [ "list" ] }, "data": { "type": "array", "items": { "type": "object", "properties": { "object": { "type": "string", "const": "client_package" }, "id": { "type": "string" }, "clientId": { "type": "string" }, "siteId": { "type": "string" }, "accessSiteIds": { "type": "array", "items": { "type": "string" } }, "packageId": { "type": "string" }, "name": { "anyOf": [ { "type": "string" }, { "type": "null" } ] }, "type": { "anyOf": [ { "type": "string", "enum": [ "PACKAGE", "MEMBERSHIP", "GUEST_PASS", "ADD_ON" ] }, { "type": "null" } ] }, "status": { "type": "string", "enum": [ "ACTIVE", "SUSPENDED", "EXPIRED", "CANCELED", "COMPLETED", "PENDING_START_DATE" ] }, "onHold": { "type": "boolean" }, "autoRenew": { "type": "boolean" }, "purchasedAt": { "type": "string", "format": "date-time", "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$" }, "startsAt": { "anyOf": [ { "type": "string" }, { "type": "null" } ] }, "expiresAt": { "anyOf": [ { "type": "string" }, { "type": "null" } ] }, "nextBillingDate": { "anyOf": [ { "type": "string" }, { "type": "null" } ] }, "price": { "anyOf": [ { "type": "string" }, { "type": "null" } ] }, "currency": { "anyOf": [ { "type": "string" }, { "type": "null" } ] }, "cancelledAt": { "anyOf": [ { "type": "string", "format": "date-time", "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$" }, { "type": "null" } ] } }, "required": [ "object", "id", "clientId", "siteId", "accessSiteIds", "packageId", "name", "type", "status", "onHold", "autoRenew", "purchasedAt", "startsAt", "expiresAt", "nextBillingDate", "price", "currency", "cancelledAt" ] } }, "hasMore": { "type": "boolean" }, "nextCursor": { "type": [ "string", "null" ] }, "url": { "type": "string" } }, "required": [ "object", "data", "hasMore", "url" ] } } } } ``` ### 400 — Malformed request ```json { "description": "Malformed request", "content": { "application/json": { "schema": { "type": "object", "properties": { "error": { "type": "string" }, "code": { "type": "string" }, "message": { "type": "string" } }, "required": [ "error" ] } } } } ``` ### 403 — The API key does not have the required scope ```json { "description": "The API key does not have the required scope", "content": { "application/json": { "schema": { "type": "object", "properties": { "error": { "type": "string" }, "code": { "type": "string" }, "message": { "type": "string" } }, "required": [ "error" ] } } } } ``` ### 404 — Not found. A record that belongs to another brand also returns 404. ```json { "description": "Not found. A record that belongs to another brand also returns 404.", "content": { "application/json": { "schema": { "type": "object", "properties": { "error": { "type": "string" }, "code": { "type": "string" }, "message": { "type": "string" } }, "required": [ "error" ] } } } } ``` ## Schemas in scope Refer to the full OpenAPI spec for resolved `$ref` definitions: - https://docs.hapana-developers.com/openapi/business-v2.json --- Source spec: `docs/openapi/hapana-business-api.openapi.json` · operationId `get_clients__id__packages` · version `v2` ## Security schemes ```json { "partnerKey": { "type": "http", "scheme": "bearer", "description": "The brand API key, e.g. `hk_test_…`. Paste the value only, without `Bearer`." } } ``` # GET /public-api/v2/clients/{id}/packages/{packageId} **Hapana Business API v2** **Base URL**: `https://public-api.hapana-app.com/public-api/v2` ## Summary Retrieve a package a client owns ## Description **Required scope:** `packages:read` **Tags**: Client packages **Authentication**: ```json [ { "partnerKey": [] } ] ``` ## Parameters | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | id | path | string | yes | | | packageId | path | string | yes | | | siteId | query | string | yes | | ## Responses ### 200 — Success ```json { "description": "Success", "content": { "application/json": { "schema": { "type": "object", "properties": { "object": { "type": "string", "const": "client_package" }, "id": { "type": "string" }, "clientId": { "type": "string" }, "siteId": { "type": "string" }, "accessSiteIds": { "type": "array", "items": { "type": "string" } }, "packageId": { "type": "string" }, "name": { "anyOf": [ { "type": "string" }, { "type": "null" } ] }, "type": { "anyOf": [ { "type": "string", "enum": [ "PACKAGE", "MEMBERSHIP", "GUEST_PASS", "ADD_ON" ] }, { "type": "null" } ] }, "status": { "type": "string", "enum": [ "ACTIVE", "SUSPENDED", "EXPIRED", "CANCELED", "COMPLETED", "PENDING_START_DATE" ] }, "onHold": { "type": "boolean" }, "autoRenew": { "type": "boolean" }, "purchasedAt": { "type": "string", "format": "date-time", "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$" }, "startsAt": { "anyOf": [ { "type": "string" }, { "type": "null" } ] }, "expiresAt": { "anyOf": [ { "type": "string" }, { "type": "null" } ] }, "nextBillingDate": { "anyOf": [ { "type": "string" }, { "type": "null" } ] }, "price": { "anyOf": [ { "type": "string" }, { "type": "null" } ] }, "currency": { "anyOf": [ { "type": "string" }, { "type": "null" } ] }, "cancelledAt": { "anyOf": [ { "type": "string", "format": "date-time", "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$" }, { "type": "null" } ] } }, "required": [ "object", "id", "clientId", "siteId", "accessSiteIds", "packageId", "name", "type", "status", "onHold", "autoRenew", "purchasedAt", "startsAt", "expiresAt", "nextBillingDate", "price", "currency", "cancelledAt" ] } } } } ``` ### 400 — Malformed request ```json { "description": "Malformed request", "content": { "application/json": { "schema": { "type": "object", "properties": { "error": { "type": "string" }, "code": { "type": "string" }, "message": { "type": "string" } }, "required": [ "error" ] } } } } ``` ### 403 — The API key does not have the required scope ```json { "description": "The API key does not have the required scope", "content": { "application/json": { "schema": { "type": "object", "properties": { "error": { "type": "string" }, "code": { "type": "string" }, "message": { "type": "string" } }, "required": [ "error" ] } } } } ``` ### 404 — Not found. A record that belongs to another brand also returns 404. ```json { "description": "Not found. A record that belongs to another brand also returns 404.", "content": { "application/json": { "schema": { "type": "object", "properties": { "error": { "type": "string" }, "code": { "type": "string" }, "message": { "type": "string" } }, "required": [ "error" ] } } } } ``` ## Schemas in scope Refer to the full OpenAPI spec for resolved `$ref` definitions: - https://docs.hapana-developers.com/openapi/business-v2.json --- Source spec: `docs/openapi/hapana-business-api.openapi.json` · operationId `get_clients__id__packages__packageId_` · version `v2` ## Security schemes ```json { "partnerKey": { "type": "http", "scheme": "bearer", "description": "The brand API key, e.g. `hk_test_…`. Paste the value only, without `Bearer`." } } ``` # POST /public-api/v2/clients/{id}/packages/{packageId}/suspend **Hapana Business API v2** **Base URL**: `https://public-api.hapana-app.com/public-api/v2` ## Summary Suspend a membership ## Description Omitting endDate pauses indefinitely. Resuming shifts the remaining billing dates forward by the paused span, so the member is never billed for time they did not have. **Required scope:** `packages:write` **Tags**: Client packages **Authentication**: ```json [ { "partnerKey": [] } ] ``` ## Parameters | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | id | path | string | yes | | | packageId | path | string | yes | | | Idempotency-Key | header | string | no | Makes the request safe to retry. A retry with the same key returns the first response; reusing a key with a different body is refused with 422. | ## Request Body ```json { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "siteId": { "type": "string", "format": "uuid", "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$" }, "startDate": { "type": "string", "format": "date", "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$" }, "endDate": { "type": "string", "format": "date", "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$" }, "reason": { "type": "string", "maxLength": 500 } }, "required": [ "siteId" ] } } } } ``` ## Responses ### 200 — Success ```json { "description": "Success", "content": { "application/json": { "schema": { "type": "object", "properties": { "object": { "type": "string", "const": "client_package" }, "id": { "type": "string" }, "clientId": { "type": "string" }, "siteId": { "type": "string" }, "accessSiteIds": { "type": "array", "items": { "type": "string" } }, "packageId": { "type": "string" }, "name": { "anyOf": [ { "type": "string" }, { "type": "null" } ] }, "type": { "anyOf": [ { "type": "string", "enum": [ "PACKAGE", "MEMBERSHIP", "GUEST_PASS", "ADD_ON" ] }, { "type": "null" } ] }, "status": { "type": "string", "enum": [ "ACTIVE", "SUSPENDED", "EXPIRED", "CANCELED", "COMPLETED", "PENDING_START_DATE" ] }, "onHold": { "type": "boolean" }, "autoRenew": { "type": "boolean" }, "purchasedAt": { "type": "string", "format": "date-time", "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$" }, "startsAt": { "anyOf": [ { "type": "string" }, { "type": "null" } ] }, "expiresAt": { "anyOf": [ { "type": "string" }, { "type": "null" } ] }, "nextBillingDate": { "anyOf": [ { "type": "string" }, { "type": "null" } ] }, "price": { "anyOf": [ { "type": "string" }, { "type": "null" } ] }, "currency": { "anyOf": [ { "type": "string" }, { "type": "null" } ] }, "cancelledAt": { "anyOf": [ { "type": "string", "format": "date-time", "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$" }, { "type": "null" } ] } }, "required": [ "object", "id", "clientId", "siteId", "accessSiteIds", "packageId", "name", "type", "status", "onHold", "autoRenew", "purchasedAt", "startsAt", "expiresAt", "nextBillingDate", "price", "currency", "cancelledAt" ] } } } } ``` ### 400 — Malformed request ```json { "description": "Malformed request", "content": { "application/json": { "schema": { "type": "object", "properties": { "error": { "type": "string" }, "code": { "type": "string" }, "message": { "type": "string" } }, "required": [ "error" ] } } } } ``` ### 403 — The API key does not have the required scope ```json { "description": "The API key does not have the required scope", "content": { "application/json": { "schema": { "type": "object", "properties": { "error": { "type": "string" }, "code": { "type": "string" }, "message": { "type": "string" } }, "required": [ "error" ] } } } } ``` ### 404 — Not found. A record that belongs to another brand also returns 404. ```json { "description": "Not found. A record that belongs to another brand also returns 404.", "content": { "application/json": { "schema": { "type": "object", "properties": { "error": { "type": "string" }, "code": { "type": "string" }, "message": { "type": "string" } }, "required": [ "error" ] } } } } ``` ### 409 — A request with this Idempotency-Key is still being processed ```json { "description": "A request with this Idempotency-Key is still being processed" } ``` ### 422 — Idempotency-Key reused with a different request body ```json { "description": "Idempotency-Key reused with a different request body" } ``` ## Schemas in scope Refer to the full OpenAPI spec for resolved `$ref` definitions: - https://docs.hapana-developers.com/openapi/business-v2.json --- Source spec: `docs/openapi/hapana-business-api.openapi.json` · operationId `post_clients__id__packages__packageId__suspend` · version `v2` ## Security schemes ```json { "partnerKey": { "type": "http", "scheme": "bearer", "description": "The brand API key, e.g. `hk_test_…`. Paste the value only, without `Bearer`." } } ``` # POST /public-api/v2/clients/{id}/packages/{packageId}/resume **Hapana Business API v2** **Base URL**: `https://public-api.hapana-app.com/public-api/v2` ## Summary Resume a suspended membership ## Description **Required scope:** `packages:write` **Tags**: Client packages **Authentication**: ```json [ { "partnerKey": [] } ] ``` ## Parameters | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | id | path | string | yes | | | packageId | path | string | yes | | | Idempotency-Key | header | string | no | Makes the request safe to retry. A retry with the same key returns the first response; reusing a key with a different body is refused with 422. | ## Request Body ```json { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "siteId": { "type": "string", "format": "uuid", "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$" }, "restartDate": { "type": "string", "format": "date", "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$" } }, "required": [ "siteId" ] } } } } ``` ## Responses ### 200 — Success ```json { "description": "Success", "content": { "application/json": { "schema": { "type": "object", "properties": { "object": { "type": "string", "const": "client_package" }, "id": { "type": "string" }, "clientId": { "type": "string" }, "siteId": { "type": "string" }, "accessSiteIds": { "type": "array", "items": { "type": "string" } }, "packageId": { "type": "string" }, "name": { "anyOf": [ { "type": "string" }, { "type": "null" } ] }, "type": { "anyOf": [ { "type": "string", "enum": [ "PACKAGE", "MEMBERSHIP", "GUEST_PASS", "ADD_ON" ] }, { "type": "null" } ] }, "status": { "type": "string", "enum": [ "ACTIVE", "SUSPENDED", "EXPIRED", "CANCELED", "COMPLETED", "PENDING_START_DATE" ] }, "onHold": { "type": "boolean" }, "autoRenew": { "type": "boolean" }, "purchasedAt": { "type": "string", "format": "date-time", "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$" }, "startsAt": { "anyOf": [ { "type": "string" }, { "type": "null" } ] }, "expiresAt": { "anyOf": [ { "type": "string" }, { "type": "null" } ] }, "nextBillingDate": { "anyOf": [ { "type": "string" }, { "type": "null" } ] }, "price": { "anyOf": [ { "type": "string" }, { "type": "null" } ] }, "currency": { "anyOf": [ { "type": "string" }, { "type": "null" } ] }, "cancelledAt": { "anyOf": [ { "type": "string", "format": "date-time", "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$" }, { "type": "null" } ] } }, "required": [ "object", "id", "clientId", "siteId", "accessSiteIds", "packageId", "name", "type", "status", "onHold", "autoRenew", "purchasedAt", "startsAt", "expiresAt", "nextBillingDate", "price", "currency", "cancelledAt" ] } } } } ``` ### 400 — Malformed request ```json { "description": "Malformed request", "content": { "application/json": { "schema": { "type": "object", "properties": { "error": { "type": "string" }, "code": { "type": "string" }, "message": { "type": "string" } }, "required": [ "error" ] } } } } ``` ### 403 — The API key does not have the required scope ```json { "description": "The API key does not have the required scope", "content": { "application/json": { "schema": { "type": "object", "properties": { "error": { "type": "string" }, "code": { "type": "string" }, "message": { "type": "string" } }, "required": [ "error" ] } } } } ``` ### 404 — Not found. A record that belongs to another brand also returns 404. ```json { "description": "Not found. A record that belongs to another brand also returns 404.", "content": { "application/json": { "schema": { "type": "object", "properties": { "error": { "type": "string" }, "code": { "type": "string" }, "message": { "type": "string" } }, "required": [ "error" ] } } } } ``` ### 409 — A request with this Idempotency-Key is still being processed ```json { "description": "A request with this Idempotency-Key is still being processed" } ``` ### 422 — Idempotency-Key reused with a different request body ```json { "description": "Idempotency-Key reused with a different request body" } ``` ## Schemas in scope Refer to the full OpenAPI spec for resolved `$ref` definitions: - https://docs.hapana-developers.com/openapi/business-v2.json --- Source spec: `docs/openapi/hapana-business-api.openapi.json` · operationId `post_clients__id__packages__packageId__resume` · version `v2` ## Security schemes ```json { "partnerKey": { "type": "http", "scheme": "bearer", "description": "The brand API key, e.g. `hk_test_…`. Paste the value only, without `Bearer`." } } ``` # POST /public-api/v2/clients/{id}/packages/{packageId}/cancel **Hapana Business API v2** **Base URL**: `https://public-api.hapana-app.com/public-api/v2` ## Summary Cancel a package or membership ## Description Honours the template’s minimum term and any cancellation charge. A future cancelAt schedules the cancellation instead of applying it now. **Required scope:** `packages:write` **Tags**: Client packages **Authentication**: ```json [ { "partnerKey": [] } ] ``` ## Parameters | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | id | path | string | yes | | | packageId | path | string | yes | | | Idempotency-Key | header | string | no | Makes the request safe to retry. A retry with the same key returns the first response; reusing a key with a different body is refused with 422. | ## Request Body ```json { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "siteId": { "type": "string", "format": "uuid", "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$" }, "reason": { "type": "string", "maxLength": 500 }, "cancelAt": { "type": "string", "format": "date", "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$" } }, "required": [ "siteId" ] } } } } ``` ## Responses ### 200 — Success ```json { "description": "Success", "content": { "application/json": { "schema": { "type": "object", "properties": { "object": { "type": "string", "const": "client_package" }, "id": { "type": "string" }, "clientId": { "type": "string" }, "siteId": { "type": "string" }, "accessSiteIds": { "type": "array", "items": { "type": "string" } }, "packageId": { "type": "string" }, "name": { "anyOf": [ { "type": "string" }, { "type": "null" } ] }, "type": { "anyOf": [ { "type": "string", "enum": [ "PACKAGE", "MEMBERSHIP", "GUEST_PASS", "ADD_ON" ] }, { "type": "null" } ] }, "status": { "type": "string", "enum": [ "ACTIVE", "SUSPENDED", "EXPIRED", "CANCELED", "COMPLETED", "PENDING_START_DATE" ] }, "onHold": { "type": "boolean" }, "autoRenew": { "type": "boolean" }, "purchasedAt": { "type": "string", "format": "date-time", "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$" }, "startsAt": { "anyOf": [ { "type": "string" }, { "type": "null" } ] }, "expiresAt": { "anyOf": [ { "type": "string" }, { "type": "null" } ] }, "nextBillingDate": { "anyOf": [ { "type": "string" }, { "type": "null" } ] }, "price": { "anyOf": [ { "type": "string" }, { "type": "null" } ] }, "currency": { "anyOf": [ { "type": "string" }, { "type": "null" } ] }, "cancelledAt": { "anyOf": [ { "type": "string", "format": "date-time", "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$" }, { "type": "null" } ] } }, "required": [ "object", "id", "clientId", "siteId", "accessSiteIds", "packageId", "name", "type", "status", "onHold", "autoRenew", "purchasedAt", "startsAt", "expiresAt", "nextBillingDate", "price", "currency", "cancelledAt" ] } } } } ``` ### 400 — Malformed request ```json { "description": "Malformed request", "content": { "application/json": { "schema": { "type": "object", "properties": { "error": { "type": "string" }, "code": { "type": "string" }, "message": { "type": "string" } }, "required": [ "error" ] } } } } ``` ### 403 — The API key does not have the required scope ```json { "description": "The API key does not have the required scope", "content": { "application/json": { "schema": { "type": "object", "properties": { "error": { "type": "string" }, "code": { "type": "string" }, "message": { "type": "string" } }, "required": [ "error" ] } } } } ``` ### 404 — Not found. A record that belongs to another brand also returns 404. ```json { "description": "Not found. A record that belongs to another brand also returns 404.", "content": { "application/json": { "schema": { "type": "object", "properties": { "error": { "type": "string" }, "code": { "type": "string" }, "message": { "type": "string" } }, "required": [ "error" ] } } } } ``` ### 409 — A request with this Idempotency-Key is still being processed ```json { "description": "A request with this Idempotency-Key is still being processed" } ``` ### 422 — Idempotency-Key reused with a different request body ```json { "description": "Idempotency-Key reused with a different request body" } ``` ## Schemas in scope Refer to the full OpenAPI spec for resolved `$ref` definitions: - https://docs.hapana-developers.com/openapi/business-v2.json --- Source spec: `docs/openapi/hapana-business-api.openapi.json` · operationId `post_clients__id__packages__packageId__cancel` · version `v2` ## Security schemes ```json { "partnerKey": { "type": "http", "scheme": "bearer", "description": "The brand API key, e.g. `hk_test_…`. Paste the value only, without `Bearer`." } } ``` # GET /public-api/v2/payments **Hapana Business API v2** **Base URL**: `https://public-api.hapana-app.com/public-api/v2` ## Summary List payments ## Description Read only, for reconciliation. Card details, stored payment methods, payment processor identifiers and fee splits are never included. **Required scope:** `payments:read` **Tags**: Payments **Authentication**: ```json [ { "partnerKey": [] } ] ``` ## Parameters | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | cursor | query | string | no | | | limit | query | integer | no | | | siteId | query | string | yes | | | clientId | query | string | no | | | startDate | query | string | yes | | | endDate | query | string | yes | | | status | query | string | no | | ## Responses ### 200 — Success ```json { "description": "Success", "content": { "application/json": { "schema": { "type": "object", "properties": { "object": { "type": "string", "enum": [ "list" ] }, "data": { "type": "array", "items": { "type": "object", "properties": { "object": { "type": "string", "const": "payment" }, "id": { "type": "string" }, "siteId": { "type": "string" }, "clientId": { "anyOf": [ { "type": "string" }, { "type": "null" } ] }, "invoiceId": { "type": "string" }, "status": { "type": "string", "enum": [ "pending", "processing", "success", "failed", "partially_refunded", "refunded", "canceled" ] }, "method": { "type": "string", "enum": [ "card", "bank", "terminal", "cash", "account_credit", "custom" ] }, "currency": { "type": "string" }, "amount": { "type": "string" }, "createdAt": { "type": "string", "format": "date-time", "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$" } }, "required": [ "object", "id", "siteId", "clientId", "invoiceId", "status", "method", "currency", "amount", "createdAt" ] } }, "hasMore": { "type": "boolean" }, "nextCursor": { "type": [ "string", "null" ] }, "url": { "type": "string" } }, "required": [ "object", "data", "hasMore", "url" ] } } } } ``` ### 400 — Malformed request ```json { "description": "Malformed request", "content": { "application/json": { "schema": { "type": "object", "properties": { "error": { "type": "string" }, "code": { "type": "string" }, "message": { "type": "string" } }, "required": [ "error" ] } } } } ``` ### 403 — The API key does not have the required scope ```json { "description": "The API key does not have the required scope", "content": { "application/json": { "schema": { "type": "object", "properties": { "error": { "type": "string" }, "code": { "type": "string" }, "message": { "type": "string" } }, "required": [ "error" ] } } } } ``` ### 404 — Not found. A record that belongs to another brand also returns 404. ```json { "description": "Not found. A record that belongs to another brand also returns 404.", "content": { "application/json": { "schema": { "type": "object", "properties": { "error": { "type": "string" }, "code": { "type": "string" }, "message": { "type": "string" } }, "required": [ "error" ] } } } } ``` ## Schemas in scope Refer to the full OpenAPI spec for resolved `$ref` definitions: - https://docs.hapana-developers.com/openapi/business-v2.json --- Source spec: `docs/openapi/hapana-business-api.openapi.json` · operationId `get_payments` · version `v2` ## Security schemes ```json { "partnerKey": { "type": "http", "scheme": "bearer", "description": "The brand API key, e.g. `hk_test_…`. Paste the value only, without `Bearer`." } } ``` # GET /public-api/v2/payments/{id} **Hapana Business API v2** **Base URL**: `https://public-api.hapana-app.com/public-api/v2` ## Summary Retrieve a payment ## Description **Required scope:** `payments:read` **Tags**: Payments **Authentication**: ```json [ { "partnerKey": [] } ] ``` ## Parameters | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | id | path | string | yes | | | siteId | query | string | yes | | ## Responses ### 200 — Success ```json { "description": "Success", "content": { "application/json": { "schema": { "type": "object", "properties": { "object": { "type": "string", "const": "payment" }, "id": { "type": "string" }, "siteId": { "type": "string" }, "clientId": { "anyOf": [ { "type": "string" }, { "type": "null" } ] }, "invoiceId": { "type": "string" }, "status": { "type": "string", "enum": [ "pending", "processing", "success", "failed", "partially_refunded", "refunded", "canceled" ] }, "method": { "type": "string", "enum": [ "card", "bank", "terminal", "cash", "account_credit", "custom" ] }, "currency": { "type": "string" }, "amount": { "type": "string" }, "createdAt": { "type": "string", "format": "date-time", "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$" } }, "required": [ "object", "id", "siteId", "clientId", "invoiceId", "status", "method", "currency", "amount", "createdAt" ] } } } } ``` ### 400 — Malformed request ```json { "description": "Malformed request", "content": { "application/json": { "schema": { "type": "object", "properties": { "error": { "type": "string" }, "code": { "type": "string" }, "message": { "type": "string" } }, "required": [ "error" ] } } } } ``` ### 403 — The API key does not have the required scope ```json { "description": "The API key does not have the required scope", "content": { "application/json": { "schema": { "type": "object", "properties": { "error": { "type": "string" }, "code": { "type": "string" }, "message": { "type": "string" } }, "required": [ "error" ] } } } } ``` ### 404 — Not found. A record that belongs to another brand also returns 404. ```json { "description": "Not found. A record that belongs to another brand also returns 404.", "content": { "application/json": { "schema": { "type": "object", "properties": { "error": { "type": "string" }, "code": { "type": "string" }, "message": { "type": "string" } }, "required": [ "error" ] } } } } ``` ## Schemas in scope Refer to the full OpenAPI spec for resolved `$ref` definitions: - https://docs.hapana-developers.com/openapi/business-v2.json --- Source spec: `docs/openapi/hapana-business-api.openapi.json` · operationId `get_payments__id_` · version `v2` ## Security schemes ```json { "partnerKey": { "type": "http", "scheme": "bearer", "description": "The brand API key, e.g. `hk_test_…`. Paste the value only, without `Bearer`." } } ``` # GET /public-api/v2/reports **Hapana Business API v2** **Base URL**: `https://public-api.hapana-app.com/public-api/v2` ## Summary List available reports ## Description Only the reports this API key is allowed to run. **Required scope:** `reports:read` **Tags**: Reports **Authentication**: ```json [ { "partnerKey": [] } ] ``` ## Responses ### 200 — Success ```json { "description": "Success", "content": { "application/json": { "schema": { "type": "object", "properties": { "object": { "type": "string", "enum": [ "list" ] }, "data": { "type": "array", "items": { "type": "object", "properties": { "object": { "type": "string", "const": "report" }, "name": { "type": "string", "enum": [ "all_clients", "client_checkin", "late_cancels", "no_shows", "first_visits", "clients_absent", "session_credits_remaining", "membership_details", "revenue_used", "net_revenue", "sales_by_service", "presold_services", "failed_payments", "failed_payment_history", "suspended_memberships", "stripe_settlement", "cancellation_report", "projected_revenue", "missing_payment_methods", "royalty_fee_distribution", "referrals", "birthdays", "session_feedback", "member_movement", "member_movement_transfers", "member_movement_transfer_summary", "new_clients", "income_handover", "delinquency", "all_invoices", "discount_usage", "memberships_expiring", "pos_history", "promo_code_usage", "expired_session_credits", "stripe_card_updater", "account_credit_balance", "ezypay_settlement", "gift_cards", "current_inventory", "refund_history", "unpaid_sessions", "amount_due", "intro_offer_conversions", "staff_utilisation", "client_lifetime_value", "client_retention_rate", "payroll", "payroll_tier_audit", "one_to_one_sessions", "session_performance", "tax_summary", "revenue_leakage", "waitlist", "client_engagement", "multi_location_comparison", "contract_agreement", "write_off_bad_debt", "content_engagement", "challenge_participation", "challenge_enrollment_detail", "challenges", "challenge_performance", "challenge_member_consolidated", "challenge_member_daily", "metric_tracking", "member_metric_detail", "leaderboard_history", "awards_summary", "video_performance", "banner_clicks", "theme_subscription_usage", "content_library_statement", "content_library_member_usage", "content_library_title_usage", "content_library_usage_detail", "content_library_usage_by_corporate", "content_library_billing_evidence", "equipment_usage", "client_equipment_usage", "upgrade_downgrade_report", "add_ons_report", "add_on_memberships", "cash_drawer", "monthly_billing_statement", "drdr_report", "guest_pass", "member_status_summary", "attendance_by_session", "commission_summary", "commission_detail", "commission_events", "staff_earnings" ] }, "label": { "type": "string" }, "description": { "type": "string" }, "category": { "type": "string", "enum": [ "client_and_attendance", "financial", "staff", "content", "commissions" ] }, "requiresDateRange": { "type": "boolean" }, "columns": { "type": "array", "items": { "type": "object", "properties": { "key": { "type": "string" }, "label": { "type": "string" }, "type": { "type": "string" } }, "required": [ "key", "label", "type" ] } } }, "required": [ "object", "name", "label", "description", "category", "requiresDateRange", "columns" ] } }, "hasMore": { "type": "boolean" }, "nextCursor": { "type": [ "string", "null" ] }, "url": { "type": "string" } }, "required": [ "object", "data", "hasMore", "url" ] } } } } ``` ### 400 — Malformed request ```json { "description": "Malformed request", "content": { "application/json": { "schema": { "type": "object", "properties": { "error": { "type": "string" }, "code": { "type": "string" }, "message": { "type": "string" } }, "required": [ "error" ] } } } } ``` ### 403 — The API key does not have the required scope ```json { "description": "The API key does not have the required scope", "content": { "application/json": { "schema": { "type": "object", "properties": { "error": { "type": "string" }, "code": { "type": "string" }, "message": { "type": "string" } }, "required": [ "error" ] } } } } ``` ### 404 — Not found. A record that belongs to another brand also returns 404. ```json { "description": "Not found. A record that belongs to another brand also returns 404.", "content": { "application/json": { "schema": { "type": "object", "properties": { "error": { "type": "string" }, "code": { "type": "string" }, "message": { "type": "string" } }, "required": [ "error" ] } } } } ``` ## Schemas in scope Refer to the full OpenAPI spec for resolved `$ref` definitions: - https://docs.hapana-developers.com/openapi/business-v2.json --- Source spec: `docs/openapi/hapana-business-api.openapi.json` · operationId `get_reports` · version `v2` ## Security schemes ```json { "partnerKey": { "type": "http", "scheme": "bearer", "description": "The brand API key, e.g. `hk_test_…`. Paste the value only, without `Bearer`." } } ``` # POST /public-api/v2/reports/run **Hapana Business API v2** **Base URL**: `https://public-api.hapana-app.com/public-api/v2` ## Summary Run a report ## Description Uses POST because the filters are structured; nothing is created. Results are paginated with a cursor. For large results, start an export instead. **Required scope:** `reports:read` **Tags**: Reports **Authentication**: ```json [ { "partnerKey": [] } ] ``` ## Parameters | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | Idempotency-Key | header | string | no | Makes the request safe to retry. A retry with the same key returns the first response; reusing a key with a different body is refused with 422. | ## Request Body ```json { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "reportName": { "type": "string", "enum": [ "all_clients", "client_checkin", "late_cancels", "no_shows", "first_visits", "clients_absent", "session_credits_remaining", "membership_details", "revenue_used", "net_revenue", "sales_by_service", "presold_services", "failed_payments", "failed_payment_history", "suspended_memberships", "stripe_settlement", "cancellation_report", "projected_revenue", "missing_payment_methods", "royalty_fee_distribution", "referrals", "birthdays", "session_feedback", "member_movement", "member_movement_transfers", "member_movement_transfer_summary", "new_clients", "income_handover", "delinquency", "all_invoices", "discount_usage", "memberships_expiring", "pos_history", "promo_code_usage", "expired_session_credits", "stripe_card_updater", "account_credit_balance", "ezypay_settlement", "gift_cards", "current_inventory", "refund_history", "unpaid_sessions", "amount_due", "intro_offer_conversions", "staff_utilisation", "client_lifetime_value", "client_retention_rate", "payroll", "payroll_tier_audit", "one_to_one_sessions", "session_performance", "tax_summary", "revenue_leakage", "waitlist", "client_engagement", "multi_location_comparison", "contract_agreement", "write_off_bad_debt", "content_engagement", "challenge_participation", "challenge_enrollment_detail", "challenges", "challenge_performance", "challenge_member_consolidated", "challenge_member_daily", "metric_tracking", "member_metric_detail", "leaderboard_history", "awards_summary", "video_performance", "banner_clicks", "theme_subscription_usage", "content_library_statement", "content_library_member_usage", "content_library_title_usage", "content_library_usage_detail", "content_library_usage_by_corporate", "content_library_billing_evidence", "equipment_usage", "client_equipment_usage", "upgrade_downgrade_report", "add_ons_report", "add_on_memberships", "cash_drawer", "monthly_billing_statement", "drdr_report", "guest_pass", "member_status_summary", "attendance_by_session", "commission_summary", "commission_detail", "commission_events", "staff_earnings" ] }, "siteIds": { "minItems": 1, "type": "array", "items": { "type": "string", "format": "uuid", "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$" } }, "dateFrom": { "type": "string", "format": "date", "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$" }, "dateTo": { "type": "string", "format": "date", "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$" }, "columns": { "type": "array", "items": { "type": "string" } }, "filters": { "type": "object", "propertyNames": { "type": "string" }, "additionalProperties": {} }, "cursor": { "type": "string" }, "pageSize": { "default": 50, "type": "integer", "minimum": 1, "maximum": 200 } }, "required": [ "reportName", "siteIds" ] } } } } ``` ## Responses ### 200 — Success ```json { "description": "Success", "content": { "application/json": { "schema": { "type": "object", "properties": { "object": { "type": "string", "const": "report_result" }, "reportName": { "type": "string" }, "columns": { "type": "array", "items": { "type": "object", "properties": { "key": { "type": "string" }, "label": { "type": "string" }, "type": { "type": "string" } }, "required": [ "key", "label", "type" ] } }, "data": { "type": "array", "items": { "type": "object", "propertyNames": { "type": "string" }, "additionalProperties": {} } }, "hasMore": { "type": "boolean" }, "nextCursor": { "anyOf": [ { "type": "string" }, { "type": "null" } ] } }, "required": [ "object", "reportName", "columns", "data", "hasMore", "nextCursor" ] } } } } ``` ### 400 — Malformed request ```json { "description": "Malformed request", "content": { "application/json": { "schema": { "type": "object", "properties": { "error": { "type": "string" }, "code": { "type": "string" }, "message": { "type": "string" } }, "required": [ "error" ] } } } } ``` ### 403 — The API key does not have the required scope ```json { "description": "The API key does not have the required scope", "content": { "application/json": { "schema": { "type": "object", "properties": { "error": { "type": "string" }, "code": { "type": "string" }, "message": { "type": "string" } }, "required": [ "error" ] } } } } ``` ### 404 — Not found. A record that belongs to another brand also returns 404. ```json { "description": "Not found. A record that belongs to another brand also returns 404.", "content": { "application/json": { "schema": { "type": "object", "properties": { "error": { "type": "string" }, "code": { "type": "string" }, "message": { "type": "string" } }, "required": [ "error" ] } } } } ``` ### 409 — A request with this Idempotency-Key is still being processed ```json { "description": "A request with this Idempotency-Key is still being processed" } ``` ### 422 — Idempotency-Key reused with a different request body ```json { "description": "Idempotency-Key reused with a different request body" } ``` ## Schemas in scope Refer to the full OpenAPI spec for resolved `$ref` definitions: - https://docs.hapana-developers.com/openapi/business-v2.json --- Source spec: `docs/openapi/hapana-business-api.openapi.json` · operationId `post_reports_run` · version `v2` ## Security schemes ```json { "partnerKey": { "type": "http", "scheme": "bearer", "description": "The brand API key, e.g. `hk_test_…`. Paste the value only, without `Bearer`." } } ``` # POST /public-api/v2/reports/exports **Hapana Business API v2** **Base URL**: `https://public-api.hapana-app.com/public-api/v2` ## Summary Start a CSV export ## Description Returns a jobId to poll, because a large export takes longer than one request. **Required scope:** `reports:read` **Tags**: Reports **Authentication**: ```json [ { "partnerKey": [] } ] ``` ## Parameters | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | Idempotency-Key | header | string | no | Makes the request safe to retry. A retry with the same key returns the first response; reusing a key with a different body is refused with 422. | ## Request Body ```json { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "reportName": { "type": "string", "enum": [ "all_clients", "client_checkin", "late_cancels", "no_shows", "first_visits", "clients_absent", "session_credits_remaining", "membership_details", "revenue_used", "net_revenue", "sales_by_service", "presold_services", "failed_payments", "failed_payment_history", "suspended_memberships", "stripe_settlement", "cancellation_report", "projected_revenue", "missing_payment_methods", "royalty_fee_distribution", "referrals", "birthdays", "session_feedback", "member_movement", "member_movement_transfers", "member_movement_transfer_summary", "new_clients", "income_handover", "delinquency", "all_invoices", "discount_usage", "memberships_expiring", "pos_history", "promo_code_usage", "expired_session_credits", "stripe_card_updater", "account_credit_balance", "ezypay_settlement", "gift_cards", "current_inventory", "refund_history", "unpaid_sessions", "amount_due", "intro_offer_conversions", "staff_utilisation", "client_lifetime_value", "client_retention_rate", "payroll", "payroll_tier_audit", "one_to_one_sessions", "session_performance", "tax_summary", "revenue_leakage", "waitlist", "client_engagement", "multi_location_comparison", "contract_agreement", "write_off_bad_debt", "content_engagement", "challenge_participation", "challenge_enrollment_detail", "challenges", "challenge_performance", "challenge_member_consolidated", "challenge_member_daily", "metric_tracking", "member_metric_detail", "leaderboard_history", "awards_summary", "video_performance", "banner_clicks", "theme_subscription_usage", "content_library_statement", "content_library_member_usage", "content_library_title_usage", "content_library_usage_detail", "content_library_usage_by_corporate", "content_library_billing_evidence", "equipment_usage", "client_equipment_usage", "upgrade_downgrade_report", "add_ons_report", "add_on_memberships", "cash_drawer", "monthly_billing_statement", "drdr_report", "guest_pass", "member_status_summary", "attendance_by_session", "commission_summary", "commission_detail", "commission_events", "staff_earnings" ] }, "siteIds": { "minItems": 1, "type": "array", "items": { "type": "string", "format": "uuid", "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$" } }, "dateFrom": { "type": "string", "format": "date", "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$" }, "dateTo": { "type": "string", "format": "date", "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$" }, "columns": { "type": "array", "items": { "type": "string" } }, "filters": { "type": "object", "propertyNames": { "type": "string" }, "additionalProperties": {} } }, "required": [ "reportName", "siteIds" ] } } } } ``` ## Responses ### 202 — Success ```json { "description": "Success", "content": { "application/json": { "schema": { "type": "object", "properties": { "object": { "type": "string", "const": "report_export" }, "jobId": { "type": "string" }, "totalRows": { "type": "integer", "minimum": -9007199254740991, "maximum": 9007199254740991 } }, "required": [ "object", "jobId", "totalRows" ] } } } } ``` ### 400 — Malformed request ```json { "description": "Malformed request", "content": { "application/json": { "schema": { "type": "object", "properties": { "error": { "type": "string" }, "code": { "type": "string" }, "message": { "type": "string" } }, "required": [ "error" ] } } } } ``` ### 403 — The API key does not have the required scope ```json { "description": "The API key does not have the required scope", "content": { "application/json": { "schema": { "type": "object", "properties": { "error": { "type": "string" }, "code": { "type": "string" }, "message": { "type": "string" } }, "required": [ "error" ] } } } } ``` ### 404 — Not found. A record that belongs to another brand also returns 404. ```json { "description": "Not found. A record that belongs to another brand also returns 404.", "content": { "application/json": { "schema": { "type": "object", "properties": { "error": { "type": "string" }, "code": { "type": "string" }, "message": { "type": "string" } }, "required": [ "error" ] } } } } ``` ### 409 — A request with this Idempotency-Key is still being processed ```json { "description": "A request with this Idempotency-Key is still being processed" } ``` ### 422 — Idempotency-Key reused with a different request body ```json { "description": "Idempotency-Key reused with a different request body" } ``` ## Schemas in scope Refer to the full OpenAPI spec for resolved `$ref` definitions: - https://docs.hapana-developers.com/openapi/business-v2.json --- Source spec: `docs/openapi/hapana-business-api.openapi.json` · operationId `post_reports_exports` · version `v2` ## Security schemes ```json { "partnerKey": { "type": "http", "scheme": "bearer", "description": "The brand API key, e.g. `hk_test_…`. Paste the value only, without `Bearer`." } } ``` # GET /public-api/v2/reports/exports/{jobId} **Hapana Business API v2** **Base URL**: `https://public-api.hapana-app.com/public-api/v2` ## Summary Poll an export job ## Description Once complete, carries a short-lived download URL. **Required scope:** `reports:read` **Tags**: Reports **Authentication**: ```json [ { "partnerKey": [] } ] ``` ## Parameters | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | jobId | path | string | yes | | ## Responses ### 200 — Success ```json { "description": "Success", "content": { "application/json": { "schema": { "type": "object", "properties": { "object": { "type": "string", "const": "report_export_status" }, "jobId": { "type": "string" }, "status": { "type": "string", "enum": [ "running", "completed", "failed" ] }, "rowsProcessed": { "type": "integer", "minimum": -9007199254740991, "maximum": 9007199254740991 }, "totalRows": { "type": "integer", "minimum": -9007199254740991, "maximum": 9007199254740991 }, "downloadUrl": { "anyOf": [ { "type": "string" }, { "type": "null" } ] }, "expiresAt": { "anyOf": [ { "type": "string" }, { "type": "null" } ] }, "error": { "anyOf": [ { "type": "string" }, { "type": "null" } ] } }, "required": [ "object", "jobId", "status", "rowsProcessed", "totalRows", "downloadUrl", "expiresAt", "error" ] } } } } ``` ### 400 — Malformed request ```json { "description": "Malformed request", "content": { "application/json": { "schema": { "type": "object", "properties": { "error": { "type": "string" }, "code": { "type": "string" }, "message": { "type": "string" } }, "required": [ "error" ] } } } } ``` ### 403 — The API key does not have the required scope ```json { "description": "The API key does not have the required scope", "content": { "application/json": { "schema": { "type": "object", "properties": { "error": { "type": "string" }, "code": { "type": "string" }, "message": { "type": "string" } }, "required": [ "error" ] } } } } ``` ### 404 — Not found. A record that belongs to another brand also returns 404. ```json { "description": "Not found. A record that belongs to another brand also returns 404.", "content": { "application/json": { "schema": { "type": "object", "properties": { "error": { "type": "string" }, "code": { "type": "string" }, "message": { "type": "string" } }, "required": [ "error" ] } } } } ``` ## Schemas in scope Refer to the full OpenAPI spec for resolved `$ref` definitions: - https://docs.hapana-developers.com/openapi/business-v2.json --- Source spec: `docs/openapi/hapana-business-api.openapi.json` · operationId `get_reports_exports__jobId_` · version `v2` ## Security schemes ```json { "partnerKey": { "type": "http", "scheme": "bearer", "description": "The brand API key, e.g. `hk_test_…`. Paste the value only, without `Bearer`." } } ``` # GET /public-api/v2/packages **Hapana Business API v2** **Base URL**: `https://public-api.hapana-app.com/public-api/v2` ## Summary List packages and memberships ## Description Active packages a site sells, with price as a decimal string. Read only. **Required scope:** `packages:read` **Tags**: Packages **Authentication**: ```json [ { "partnerKey": [] } ] ``` ## Parameters | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | siteId | query | string | yes | | | type | query | string | no | | | introOffer | query | string | no | | ## Responses ### 200 — Success ```json { "description": "Success", "content": { "application/json": { "schema": { "type": "object", "properties": { "object": { "type": "string", "enum": [ "list" ] }, "data": { "type": "array", "items": { "type": "object", "properties": { "object": { "type": "string", "const": "package" }, "id": { "type": "string" }, "siteId": { "type": "string" }, "name": { "type": "string" }, "description": { "anyOf": [ { "type": "string" }, { "type": "null" } ] }, "type": { "type": "string", "enum": [ "PACKAGE", "MEMBERSHIP", "GUEST_PASS", "ADD_ON" ] }, "price": { "type": "string" }, "introOffer": { "type": "boolean" }, "availableOnline": { "type": "boolean" }, "length": { "anyOf": [ { "type": "object", "properties": { "amount": { "type": "integer", "minimum": -9007199254740991, "maximum": 9007199254740991 }, "unit": { "type": "string", "enum": [ "YEARS", "MONTHS", "WEEKS", "DAYS" ] } }, "required": [ "amount", "unit" ] }, { "type": "null" } ] }, "billingCycle": { "anyOf": [ { "type": "object", "properties": { "amount": { "type": "integer", "minimum": -9007199254740991, "maximum": 9007199254740991 }, "unit": { "type": "string", "enum": [ "YEARS", "MONTHS", "WEEKS" ] } }, "required": [ "amount", "unit" ] }, { "type": "null" } ] }, "credits": { "type": "array", "items": { "type": "object", "properties": { "type": { "type": "string", "enum": [ "UNLIMITED", "LIMITED" ] }, "amount": { "anyOf": [ { "type": "integer", "minimum": -9007199254740991, "maximum": 9007199254740991 }, { "type": "null" } ] }, "sessionTypeIds": { "type": "array", "items": { "type": "string" } } }, "required": [ "type", "amount", "sessionTypeIds" ] } } }, "required": [ "object", "id", "siteId", "name", "description", "type", "price", "introOffer", "availableOnline", "length", "billingCycle", "credits" ] } }, "hasMore": { "type": "boolean" }, "nextCursor": { "type": [ "string", "null" ] }, "url": { "type": "string" } }, "required": [ "object", "data", "hasMore", "url" ] } } } } ``` ### 400 — Malformed request ```json { "description": "Malformed request", "content": { "application/json": { "schema": { "type": "object", "properties": { "error": { "type": "string" }, "code": { "type": "string" }, "message": { "type": "string" } }, "required": [ "error" ] } } } } ``` ### 403 — The API key does not have the required scope ```json { "description": "The API key does not have the required scope", "content": { "application/json": { "schema": { "type": "object", "properties": { "error": { "type": "string" }, "code": { "type": "string" }, "message": { "type": "string" } }, "required": [ "error" ] } } } } ``` ### 404 — Not found. A record that belongs to another brand also returns 404. ```json { "description": "Not found. A record that belongs to another brand also returns 404.", "content": { "application/json": { "schema": { "type": "object", "properties": { "error": { "type": "string" }, "code": { "type": "string" }, "message": { "type": "string" } }, "required": [ "error" ] } } } } ``` ## Schemas in scope Refer to the full OpenAPI spec for resolved `$ref` definitions: - https://docs.hapana-developers.com/openapi/business-v2.json --- Source spec: `docs/openapi/hapana-business-api.openapi.json` · operationId `get_packages` · version `v2` ## Security schemes ```json { "partnerKey": { "type": "http", "scheme": "bearer", "description": "The brand API key, e.g. `hk_test_…`. Paste the value only, without `Bearer`." } } ``` # GET /public-api/v2/staff-access-policies **Hapana Business API v2** **Base URL**: `https://public-api.hapana-app.com/public-api/v2` ## Summary List assignable access policies ## Description Id and name only. Lists the policies this API key is allowed to assign. **Required scope:** `staff:read` **Tags**: Reference **Authentication**: ```json [ { "partnerKey": [] } ] ``` ## Responses ### 200 — Success ```json { "description": "Success", "content": { "application/json": { "schema": { "type": "object", "properties": { "object": { "type": "string", "enum": [ "list" ] }, "data": { "type": "array", "items": { "type": "object", "properties": { "object": { "type": "string", "const": "staff_access_policy" }, "id": { "type": "string", "format": "uuid", "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$" }, "name": { "type": "string" } }, "required": [ "object", "id", "name" ] } }, "hasMore": { "type": "boolean" }, "nextCursor": { "type": [ "string", "null" ] }, "url": { "type": "string" } }, "required": [ "object", "data", "hasMore", "url" ] } } } } ``` ### 400 — Malformed request ```json { "description": "Malformed request", "content": { "application/json": { "schema": { "type": "object", "properties": { "error": { "type": "string" }, "code": { "type": "string" }, "message": { "type": "string" } }, "required": [ "error" ] } } } } ``` ### 403 — The API key does not have the required scope ```json { "description": "The API key does not have the required scope", "content": { "application/json": { "schema": { "type": "object", "properties": { "error": { "type": "string" }, "code": { "type": "string" }, "message": { "type": "string" } }, "required": [ "error" ] } } } } ``` ### 404 — Not found. A record that belongs to another brand also returns 404. ```json { "description": "Not found. A record that belongs to another brand also returns 404.", "content": { "application/json": { "schema": { "type": "object", "properties": { "error": { "type": "string" }, "code": { "type": "string" }, "message": { "type": "string" } }, "required": [ "error" ] } } } } ``` ## Schemas in scope Refer to the full OpenAPI spec for resolved `$ref` definitions: - https://docs.hapana-developers.com/openapi/business-v2.json --- Source spec: `docs/openapi/hapana-business-api.openapi.json` · operationId `get_staff_access_policies` · version `v2` ## Security schemes ```json { "partnerKey": { "type": "http", "scheme": "bearer", "description": "The brand API key, e.g. `hk_test_…`. Paste the value only, without `Bearer`." } } ``` # GET /public-api/v2/locations **Hapana Business API v2** **Base URL**: `https://public-api.hapana-app.com/public-api/v2` ## Summary List locations within a site ## Description **Required scope:** `sites:read` **Tags**: Reference **Authentication**: ```json [ { "partnerKey": [] } ] ``` ## Parameters | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | siteId | query | string | yes | | ## Responses ### 200 — Success ```json { "description": "Success", "content": { "application/json": { "schema": { "type": "object", "properties": { "object": { "type": "string", "enum": [ "list" ] }, "data": { "type": "array", "items": { "type": "object", "properties": { "object": { "type": "string", "const": "location" }, "id": { "type": "string", "format": "uuid", "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$" }, "siteId": { "type": "string", "format": "uuid", "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$" }, "name": { "type": "string" }, "type": { "anyOf": [ { "type": "string" }, { "type": "null" } ] }, "isDefault": { "type": "boolean" } }, "required": [ "object", "id", "siteId", "name", "type", "isDefault" ] } }, "hasMore": { "type": "boolean" }, "nextCursor": { "type": [ "string", "null" ] }, "url": { "type": "string" } }, "required": [ "object", "data", "hasMore", "url" ] } } } } ``` ### 400 — Malformed request ```json { "description": "Malformed request", "content": { "application/json": { "schema": { "type": "object", "properties": { "error": { "type": "string" }, "code": { "type": "string" }, "message": { "type": "string" } }, "required": [ "error" ] } } } } ``` ### 403 — The API key does not have the required scope ```json { "description": "The API key does not have the required scope", "content": { "application/json": { "schema": { "type": "object", "properties": { "error": { "type": "string" }, "code": { "type": "string" }, "message": { "type": "string" } }, "required": [ "error" ] } } } } ``` ### 404 — Not found. A record that belongs to another brand also returns 404. ```json { "description": "Not found. A record that belongs to another brand also returns 404.", "content": { "application/json": { "schema": { "type": "object", "properties": { "error": { "type": "string" }, "code": { "type": "string" }, "message": { "type": "string" } }, "required": [ "error" ] } } } } ``` ## Schemas in scope Refer to the full OpenAPI spec for resolved `$ref` definitions: - https://docs.hapana-developers.com/openapi/business-v2.json --- Source spec: `docs/openapi/hapana-business-api.openapi.json` · operationId `get_locations` · version `v2` ## Security schemes ```json { "partnerKey": { "type": "http", "scheme": "bearer", "description": "The brand API key, e.g. `hk_test_…`. Paste the value only, without `Bearer`." } } ``` # GET /public-api/v2/business/access/checkin-eligibility **Hapana Business API v2** **Base URL**: `https://public-api.hapana-app.com/public-api/v2` ## Summary List check-in eligibility ## Description Whether each member at a site may check in right now, for a door system to cache. Without `updatedSince` it returns every member with a verdict; with it, only the verdicts that changed since then. Denied members are rows too, so a member missing from the list has no verdict yet; never treat that as eligible. Follow `nextCursor` while `hasMore` is true; a page can hold fewer than `limit` entries. When your door system has no active door registered at the site, the request is refused with 403 and `code: PROVIDER_NOT_ACTIVE_AT_SITE`; stop pulling that site until a door is registered. **Required scope:** `access:read` **Tags**: Door access **Authentication**: ```json [ { "partnerKey": [] } ] ``` ## Parameters | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | siteId | query | string | yes | | | updatedSince | query | string | no | | | cursor | query | string | no | | | limit | query | integer | no | | ## Responses ### 200 — Success ```json { "description": "Success", "content": { "application/json": { "schema": { "type": "object", "properties": { "object": { "type": "string", "enum": [ "list" ] }, "data": { "type": "array", "items": { "type": "object", "properties": { "object": { "type": "string", "const": "checkin_eligibility" }, "memberId": { "type": "string" }, "barcode": { "anyOf": [ { "type": "string" }, { "type": "null" } ] }, "credentials": { "type": "array", "items": { "type": "object", "properties": { "type": { "type": "string", "enum": [ "BARCODE" ] }, "value": { "type": "string" } }, "required": [ "type", "value" ] } }, "canCheckIn": { "type": "boolean" }, "reason": { "type": "string", "enum": [ "OK", "NO_CREDITS", "CHECKIN_DISABLED", "PAYMENT_FAIL", "SUSPENDED", "MEMBER_BLOCKED", "NO_ACTIVE_MEMBERSHIP", "ACCESS_HOURS", "ZONE_SCHEDULE", "ACCESS_DENIED", "DOOR_NOT_ACTIVE" ] }, "doors": { "type": "object", "propertyNames": { "type": "string" }, "additionalProperties": { "type": "object", "properties": { "canCheckIn": { "type": "boolean" }, "reason": { "type": "string", "enum": [ "OK", "NO_CREDITS", "CHECKIN_DISABLED", "PAYMENT_FAIL", "SUSPENDED", "MEMBER_BLOCKED", "NO_ACTIVE_MEMBERSHIP", "ACCESS_HOURS", "ZONE_SCHEDULE", "ACCESS_DENIED", "DOOR_NOT_ACTIVE" ] } }, "required": [ "canCheckIn", "reason" ] } }, "updatedAt": { "type": "string", "format": "date-time", "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$" } }, "required": [ "object", "memberId", "barcode", "credentials", "canCheckIn", "reason", "doors", "updatedAt" ] } }, "hasMore": { "type": "boolean" }, "nextCursor": { "type": [ "string", "null" ] }, "url": { "type": "string" } }, "required": [ "object", "data", "hasMore", "url" ] } } } } ``` ### 400 — Malformed request ```json { "description": "Malformed request", "content": { "application/json": { "schema": { "type": "object", "properties": { "error": { "type": "string" }, "code": { "type": "string" }, "message": { "type": "string" } }, "required": [ "error" ] } } } } ``` ### 403 — The API key does not have the required scope (`code: INSUFFICIENT_SCOPE`), or the site has no active door registered for partner access (`code: PROVIDER_NOT_ACTIVE_AT_SITE`) ```json { "description": "The API key does not have the required scope (`code: INSUFFICIENT_SCOPE`), or the site has no active door registered for partner access (`code: PROVIDER_NOT_ACTIVE_AT_SITE`)", "content": { "application/json": { "schema": { "type": "object", "properties": { "error": { "type": "string" }, "code": { "type": "string" }, "message": { "type": "string" } }, "required": [ "error" ] } } } } ``` ### 404 — Not found. A record that belongs to another brand also returns 404. ```json { "description": "Not found. A record that belongs to another brand also returns 404.", "content": { "application/json": { "schema": { "type": "object", "properties": { "error": { "type": "string" }, "code": { "type": "string" }, "message": { "type": "string" } }, "required": [ "error" ] } } } } ``` ## Schemas in scope Refer to the full OpenAPI spec for resolved `$ref` definitions: - https://docs.hapana-developers.com/openapi/business-v2.json --- Source spec: `docs/openapi/hapana-business-api.openapi.json` · operationId `get_business_access_checkin_eligibility` · version `v2` ## Security schemes ```json { "partnerKey": { "type": "http", "scheme": "bearer", "description": "The brand API key, e.g. `hk_test_…`. Paste the value only, without `Bearer`." } } ``` # GET /public-api/v2/business/access/doors **Hapana Business API v2** **Base URL**: `https://public-api.hapana-app.com/public-api/v2` ## Summary List doors at a site ## Description The door-access partner doors at a site, with the `doorId` to send on each tap and to read from the eligibility feed's `doors` map. Map each one to your own device. Inactive doors stay listed with `status: INACTIVE`; removed doors do not appear. A site with no partner door returns an empty list. The list is not paginated. Until door listing is switched on for your account the call answers 404 with `code: FEATURE_NOT_ENABLED`. **Required scope:** `access:read` **Tags**: Door access **Authentication**: ```json [ { "partnerKey": [] } ] ``` ## Parameters | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | siteId | query | string | yes | | ## Responses ### 200 — Success ```json { "description": "Success", "content": { "application/json": { "schema": { "type": "object", "properties": { "object": { "type": "string", "enum": [ "list" ] }, "data": { "type": "array", "items": { "type": "object", "properties": { "object": { "type": "string", "const": "door" }, "doorId": { "type": "string" }, "siteId": { "type": "string" }, "name": { "type": "string" }, "status": { "type": "string", "enum": [ "ACTIVE", "INACTIVE" ] }, "isMainEntrance": { "type": "boolean" }, "zone": { "anyOf": [ { "type": "object", "properties": { "zoneId": { "type": "string" }, "name": { "type": "string" }, "entryMode": { "type": "string", "enum": [ "ACCESS_ONLY", "CHECK_IN" ] }, "walkIn": { "type": "string", "enum": [ "ALLOWED", "BOOKING_ONLY" ] }, "accessSchedule": { "anyOf": [ { "type": "object", "properties": { "mode": { "type": "string", "enum": [ "TWENTY_FOUR_HOUR", "BUSINESS_HOURS", "CUSTOM" ] } }, "required": [ "mode" ] }, { "type": "null" } ] }, "sessionTypeIds": { "type": "array", "items": { "type": "string" } }, "rooms": { "type": "array", "items": { "type": "object", "properties": { "siteLocationId": { "type": "string" }, "siteLocationRoomId": { "type": "string" } }, "required": [ "siteLocationId", "siteLocationRoomId" ] } } }, "required": [ "zoneId", "name", "entryMode", "walkIn", "accessSchedule", "sessionTypeIds", "rooms" ] }, { "type": "null" } ] }, "updatedAt": { "type": "string", "format": "date-time", "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$" } }, "required": [ "object", "doorId", "siteId", "name", "status", "isMainEntrance", "zone", "updatedAt" ] } }, "hasMore": { "type": "boolean" }, "nextCursor": { "type": [ "string", "null" ] }, "url": { "type": "string" } }, "required": [ "object", "data", "hasMore", "url" ] } } } } ``` ### 400 — Malformed request ```json { "description": "Malformed request", "content": { "application/json": { "schema": { "type": "object", "properties": { "error": { "type": "string" }, "code": { "type": "string" }, "message": { "type": "string" } }, "required": [ "error" ] } } } } ``` ### 403 — The API key does not have the required scope ```json { "description": "The API key does not have the required scope", "content": { "application/json": { "schema": { "type": "object", "properties": { "error": { "type": "string" }, "code": { "type": "string" }, "message": { "type": "string" } }, "required": [ "error" ] } } } } ``` ### 404 — Not found. A record that belongs to another brand also returns 404. ```json { "description": "Not found. A record that belongs to another brand also returns 404.", "content": { "application/json": { "schema": { "type": "object", "properties": { "error": { "type": "string" }, "code": { "type": "string" }, "message": { "type": "string" } }, "required": [ "error" ] } } } } ``` ## Schemas in scope Refer to the full OpenAPI spec for resolved `$ref` definitions: - https://docs.hapana-developers.com/openapi/business-v2.json --- Source spec: `docs/openapi/hapana-business-api.openapi.json` · operationId `get_business_access_doors` · version `v2` ## Security schemes ```json { "partnerKey": { "type": "http", "scheme": "bearer", "description": "The brand API key, e.g. `hk_test_…`. Paste the value only, without `Bearer`." } } ``` # POST /public-api/v2/business/access/checkin **Hapana Business API v2** **Base URL**: `https://public-api.hapana-app.com/public-api/v2` ## Summary Check a member in at a door ## Description Call on every tap. Hapana decides whether the member may enter and, if so, records the check-in and uses a credit when the membership needs one. A refusal is still a 200, with `canCheckIn: false` and a `reason`. A tap at a site where your door system has no active door, or naming a `doorId` that is not one of your active doors, is refused with `reason: DOOR_NOT_ACTIVE` and nothing is recorded. Send `barcode` or `memberId`. Send `occurredAt` when replaying a tap your door decided offline; it may be at most 5 minutes in the future and 7 days in the past, otherwise the tap is refused with 422. **Required scope:** `access:write` **Tags**: Door access **Authentication**: ```json [ { "partnerKey": [] } ] ``` ## Parameters | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | Idempotency-Key | header | string | no | Makes the request safe to retry. A retry with the same key returns the first response; reusing a key with a different body is refused with 422. | ## Request Body ```json { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "siteId": { "type": "string", "format": "uuid", "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$" }, "barcode": { "type": "string", "minLength": 1 }, "memberId": { "type": "string", "minLength": 1 }, "doorId": { "type": "string", "minLength": 1 }, "occurredAt": { "type": "string", "format": "date-time", "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$" }, "doorDecision": { "type": "string", "enum": [ "ALLOW", "DENY" ] }, "doorDecisionReason": { "type": "string", "minLength": 1, "maxLength": 200 } }, "required": [ "siteId" ] } } } } ``` ## Responses ### 200 — Success ```json { "description": "Success", "content": { "application/json": { "schema": { "type": "object", "properties": { "object": { "type": "string", "const": "checkin_result" }, "canCheckIn": { "type": "boolean" }, "reason": { "type": "string", "enum": [ "OK", "NO_CREDITS", "CHECKIN_DISABLED", "PAYMENT_FAIL", "SUSPENDED", "MEMBER_BLOCKED", "NO_ACTIVE_MEMBERSHIP", "ACCESS_HOURS", "ZONE_SCHEDULE", "ACCESS_DENIED", "DOOR_NOT_ACTIVE" ] }, "checkInId": { "anyOf": [ { "type": "string" }, { "type": "null" } ] }, "timestamp": { "type": "string", "format": "date-time", "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$" }, "creditsRemaining": { "anyOf": [ { "type": "integer", "minimum": -9007199254740991, "maximum": 9007199254740991 }, { "type": "null" } ] }, "creditsAtRenewal": { "anyOf": [ { "type": "integer", "minimum": -9007199254740991, "maximum": 9007199254740991 }, { "type": "null" } ] }, "renewalAt": { "anyOf": [ { "type": "string", "format": "date-time", "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$" }, { "type": "null" } ] }, "isUnlimited": { "anyOf": [ { "type": "boolean" }, { "type": "null" } ] } }, "required": [ "object", "canCheckIn", "reason", "checkInId", "timestamp", "creditsRemaining", "creditsAtRenewal", "renewalAt", "isUnlimited" ] } } } } ``` ### 400 — Malformed request ```json { "description": "Malformed request", "content": { "application/json": { "schema": { "type": "object", "properties": { "error": { "type": "string" }, "code": { "type": "string" }, "message": { "type": "string" } }, "required": [ "error" ] } } } } ``` ### 403 — The API key does not have the required scope ```json { "description": "The API key does not have the required scope", "content": { "application/json": { "schema": { "type": "object", "properties": { "error": { "type": "string" }, "code": { "type": "string" }, "message": { "type": "string" } }, "required": [ "error" ] } } } } ``` ### 404 — Not found. A record that belongs to another brand also returns 404. ```json { "description": "Not found. A record that belongs to another brand also returns 404.", "content": { "application/json": { "schema": { "type": "object", "properties": { "error": { "type": "string" }, "code": { "type": "string" }, "message": { "type": "string" } }, "required": [ "error" ] } } } } ``` ### 409 — A request with this Idempotency-Key is still being processed ```json { "description": "A request with this Idempotency-Key is still being processed" } ``` ### 422 — Idempotency-Key reused with a different request body ```json { "description": "Idempotency-Key reused with a different request body" } ``` ## Schemas in scope Refer to the full OpenAPI spec for resolved `$ref` definitions: - https://docs.hapana-developers.com/openapi/business-v2.json --- Source spec: `docs/openapi/hapana-business-api.openapi.json` · operationId `post_business_access_checkin` · version `v2` ## Security schemes ```json { "partnerKey": { "type": "http", "scheme": "bearer", "description": "The brand API key, e.g. `hk_test_…`. Paste the value only, without `Bearer`." } } ``` # GET /public-api/v2/business/access/bookings **Hapana Business API v2** **Base URL**: `https://public-api.hapana-app.com/public-api/v2` ## Summary List upcoming bookings for door pre-loading ## Description One row per booking for the sessions starting in `[from, from + days)` at a site, with the session embedded and the `entryWindow` the door will honour, so a panel can hold a member before class. `from` defaults to two hours before now, so a class in progress is still served, and may be at most 7 days back; `days` defaults to 7 (max 14). Live rows are `CONFIRMED` or `ATTENDED`; a cancelled booking, late cancel, cancelled class or deleted class all come back as `CANCELLED`, which removes the pre-load. With `changedSince` only rows whose booking or session changed after it are returned; echo the response `serverTime` minus five minutes as the next `changedSince` and upsert by `bookingId`, since rows across the overlap repeat. Do a full pull without `changedSince` once a day and treat it as the complete set for the window: a booking absent from it has left the feed (moved out of the window, or no longer CONFIRMED or ATTENDED) and should be dropped. A cursor pins the window and the `serverTime` of the walk it was issued for: later pages ignore `from`, `days` and `changedSince` and return the first page `serverTime`, so use that value for the next delta even if the walk paused. A cursor older than a day or outside the feed limits is a 400; start a new walk. Check-in setting changes return the whole window on the next delta, and an ATTENDED row carries the re-entry window (2 hours before start to session end). Timestamps with a `+` offset must be URL-encoded. Follow `nextCursor` while `hasMore` is true; a malformed cursor is a 400. **Required scope:** `access:read` **Tags**: Door access **Authentication**: ```json [ { "partnerKey": [] } ] ``` ## Parameters | Name | In | Type | Required | Description | |------|-----|------|----------|-------------| | siteId | query | string | yes | | | from | query | string | no | | | days | query | integer | no | | | changedSince | query | string | no | | | cursor | query | string | no | | | limit | query | integer | no | | ## Responses ### 200 — Success ```json { "description": "Success", "content": { "application/json": { "schema": { "type": "object", "properties": { "object": { "type": "string", "enum": [ "list" ] }, "data": { "type": "array", "items": { "type": "object", "properties": { "object": { "type": "string", "const": "booking_feed_entry" }, "bookingId": { "type": "string" }, "status": { "type": "string", "enum": [ "CONFIRMED", "ATTENDED", "CANCELLED" ] }, "member": { "type": "object", "properties": { "memberId": { "type": "string" }, "barcode": { "anyOf": [ { "type": "string" }, { "type": "null" } ] } }, "required": [ "memberId", "barcode" ] }, "session": { "type": "object", "properties": { "sessionId": { "type": "string" }, "name": { "type": "string" }, "sessionTypeId": { "type": "string" }, "status": { "type": "string", "enum": [ "SCHEDULED", "CANCELLED" ] }, "startTime": { "type": "string", "format": "date-time", "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$" }, "endTime": { "type": "string", "format": "date-time", "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$" }, "timezone": { "type": "string" }, "roomId": { "anyOf": [ { "type": "string" }, { "type": "null" } ] }, "locationId": { "anyOf": [ { "type": "string" }, { "type": "null" } ] } }, "required": [ "sessionId", "name", "sessionTypeId", "status", "startTime", "endTime", "timezone", "roomId", "locationId" ] }, "entryWindow": { "type": "object", "properties": { "from": { "type": "string", "format": "date-time", "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$" }, "to": { "type": "string", "format": "date-time", "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$" } }, "required": [ "from", "to" ] }, "updatedAt": { "type": "string", "format": "date-time", "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$" } }, "required": [ "object", "bookingId", "status", "member", "session", "entryWindow", "updatedAt" ] } }, "hasMore": { "type": "boolean" }, "nextCursor": { "type": [ "string", "null" ] }, "url": { "type": "string" }, "serverTime": { "type": "string", "format": "date-time", "description": "When the walk started (the same on every page); pass it back minus an overlap as changedSince." } }, "required": [ "object", "data", "hasMore", "url", "serverTime" ] } } } } ``` ### 400 — Malformed request ```json { "description": "Malformed request", "content": { "application/json": { "schema": { "type": "object", "properties": { "error": { "type": "string" }, "code": { "type": "string" }, "message": { "type": "string" } }, "required": [ "error" ] } } } } ``` ### 403 — The API key does not have the required scope ```json { "description": "The API key does not have the required scope", "content": { "application/json": { "schema": { "type": "object", "properties": { "error": { "type": "string" }, "code": { "type": "string" }, "message": { "type": "string" } }, "required": [ "error" ] } } } } ``` ### 404 — Not found. A record that belongs to another brand also returns 404. ```json { "description": "Not found. A record that belongs to another brand also returns 404.", "content": { "application/json": { "schema": { "type": "object", "properties": { "error": { "type": "string" }, "code": { "type": "string" }, "message": { "type": "string" } }, "required": [ "error" ] } } } } ``` ## Schemas in scope Refer to the full OpenAPI spec for resolved `$ref` definitions: - https://docs.hapana-developers.com/openapi/business-v2.json --- Source spec: `docs/openapi/hapana-business-api.openapi.json` · operationId `get_business_access_bookings` · version `v2` ## Security schemes ```json { "partnerKey": { "type": "http", "scheme": "bearer", "description": "The brand API key, e.g. `hk_test_…`. Paste the value only, without `Bearer`." } } ``` --- # Guides # Webhooks Webhooks push events to your server as they happen in Hapana (a member signs up, a booking is made, someone checks in) so you don't have to poll the API. Each delivery is a signed HTTPS `POST` with a JSON body. Human-readable version: https://docs.hapana-developers.com/guides/webhooks/ ## How it works - **Brand-level.** Webhooks are configured once for the whole brand, not per location. Each webhook chooses which locations it hears from. - **Per-endpoint delivery.** Each webhook gets its own delivery and its own retries, so one slow endpoint never delays another. - **After the fact.** Events are sent once the change has been saved, so reading the record back through the API straight away returns the state the event announced. ## Setting up a webhook in Core Webhooks are managed by brand owners and admins: anyone whose role has the **Manage integrations** permission. 1. In Hapana Core, open **Control Center → Developer APIs → Webhooks**. 2. Click **Add webhook** and name it. 3. Enter the **Webhook URL**. It must be a public `https://` address; private and internal network addresses are refused. 4. Choose the **events** to subscribe to (see [events.md](./events.md)). 5. Choose the **locations**: **All locations** (includes locations added later) or **Selected locations only** (new locations are not added automatically). 6. Optionally add a **custom header** (name and value), sent with every delivery. The name must be a valid HTTP header name and cannot be `Host`, `Content-Type`, `Content-Length`, `Authorization`, or start with `X-Hapana-`. 7. Save. The **signing secret** (`whsec_...`) is shown **once**. Copy it into your secret store. If it is lost, use **Rotate secret**. ## Delivery ``` POST /hapana/webhooks HTTP/1.1 Content-Type: application/json X-Hapana-Event-ID: evt_7c1e1c52-3f0a-4a8e-9a43-2f5b1d0e6c11 X-Hapana-Event-Type: booking.created X-Hapana-Timestamp: 1790982502 X-Hapana-Signature: t=1790982502,v1=48373c7bd723348cf7520b12718567bdd0950ea5f913dbc30177a0287c732c27 { "id": "evt_7c1e1c52-3f0a-4a8e-9a43-2f5b1d0e6c11", "object": "event", "type": "booking.created", "apiVersion": "2026-01-10", "createdAt": "2026-10-02T23:08:22.481Z", "livemode": true, "data": { "bookingId": "...", "clientId": "...", "sessionId": "...", "siteId": "...", "status": "CONFIRMED", "bookingSource": "CREDIT" } } ``` ### Envelope | Field | Type | Description | | -- | -- | -- | | `id` | string | `evt_` + UUID. Same on every retry; use it for idempotency. | | `object` | string | Always `"event"`. | | `type` | string | Event type, e.g. `booking.created`. | | `apiVersion` | string | Envelope version, currently `2026-01-10`. | | `createdAt` | string | ISO 8601 UTC time the event was raised. | | `livemode` | boolean | Always `true`. | | `data` | object | Event-specific payload (see [events.md](./events.md)). | The legacy v1 `checkIn` event is the exception: it is sent as the bare v1 body, with no envelope. Payloads carry ids and changed fields, not full records; read the record back through the Business API for more. New fields may be added to `data` at any time; ignore fields you don't recognise. ### Headers | Header | Meaning | | -- | -- | | `Content-Type` | `application/json` | | `X-Hapana-Event-ID` | Event id (matches body `id`). Same across retries. | | `X-Hapana-Event-Type` | Event type. | | `X-Hapana-Timestamp` | Unix seconds when this attempt was signed. | | `X-Hapana-Signature` | `t=,v1=`. See [verifying-signatures.md](./verifying-signatures.md). | | your custom header | Only if configured. | ## Responding, timeouts and retries Respond with any `2xx` within **10 seconds**. The response body is ignored. A non-`2xx` status (including `4xx`), a timeout, a connection/TLS error, or a `3xx` redirect (redirects are never followed) is a failed attempt. Each event is attempted up to **3 times**: the original delivery, then about 1 minute later, then about 2 minutes after that (exponential backoff). Every attempt is signed afresh with a current timestamp. Acknowledge fast and process asynchronously. ## Webhook status Hapana counts consecutive **events** that failed after all retries (not individual attempts). | Status | When | Deliveries | | -- | -- | -- | | Enabled | Normal | Sent | | Failing | 3 events in a row failed | Still sent; the next success returns it to Enabled | | Disabled | 10 events in a row failed | Stopped. Fix the endpoint, then click **Re-enable** in Core | Events raised while a webhook is disabled are not queued or replayed. ## Delivery history Every attempt (event type, event id, endpoint, response status, attempt number, latency, error) is recorded. See the webhook's recent deliveries in Core, or the full history under **Control Center → Developer APIs → Logging**. ## Idempotency and ordering Delivery is **at least once**. Record each `X-Hapana-Event-ID` you process and skip repeats. Events are **not guaranteed to arrive in order**; use `createdAt`, or read current state from the API, when order matters. ## Rotating the secret **Rotate secret** in Core issues a new secret (shown once) and **the old one stops working immediately**. For zero downtime, have your verifier accept either secret, rotate, deploy the new one, then drop the old. ## Testing 1. Expose a local server over HTTPS (`ngrok http 3000`, `cloudflared tunnel`) or use a request inspector. 2. Create a webhook pointing at it, scoped to one test location (**Selected locations only**). 3. Trigger a real event there (create a member, book a class, check someone in). 4. Confirm the delivery in the webhook's recent deliveries and in **Developer APIs → Logging**. 5. Unit-test your verifier with the test vector in [verifying-signatures.md](./verifying-signatures.md). ## Migrating from Hapana v1 Partners built on the v1 `checkIn` webhook (for example Realtime Feedback) can subscribe to the legacy `checkIn` event, which sends the exact v1 body. See [events.md](./events.md#legacy-v1-check-in-checkin). # Verify webhook signatures Hapana signs every webhook delivery with HMAC-SHA256 using the webhook's signing secret. Verify the signature before you trust the payload: it proves the request came from Hapana and wasn't altered. Human-readable version: https://docs.hapana-developers.com/guides/webhooks/verifying-signatures/ ## The signature header ``` X-Hapana-Timestamp: 1790982502 X-Hapana-Signature: t=1790982502,v1=48373c7bd723348cf7520b12718567bdd0950ea5f913dbc30177a0287c732c27 ``` `X-Hapana-Signature` is a comma-separated list of `key=value` pairs: `t` is the Unix timestamp (seconds) the attempt was signed at (same as `X-Hapana-Timestamp`) and `v1` is the lowercase hex HMAC-SHA256. Parse the pairs rather than relying on position, and ignore unknown keys. ## How to verify 1. Read the **raw request body** exactly as received. Don't parse and re-serialise it. 2. Parse `t` and `v1` from `X-Hapana-Signature`. 3. Build the signed payload: `${t}.${rawBody}`. 4. Compute `HMAC-SHA256(secret, payload)` and hex-encode. The key is the **whole secret string including the `whsec_` prefix**, as UTF-8 bytes. 5. Compare with `v1` in **constant time**. 6. Reject if `t` is more than **5 minutes** from your clock. Retries are re-signed with a fresh timestamp, so legitimate retries pass. Respond `400`/`401` to a request that fails verification and don't process it. ## Node.js ```js import crypto from 'node:crypto' import express from 'express' const SECRET = process.env.HAPANA_WEBHOOK_SECRET // whsec_... const TOLERANCE_SECONDS = 300 function parseSignature(header) { const parts = { t: null, v1: [] } for (const pair of header.split(',')) { const [key, value] = pair.trim().split('=', 2) if (key === 't') parts.t = value if (key === 'v1') parts.v1.push(value) } return parts } export function verifyHapanaSignature(rawBody, header, secret = SECRET) { const { t, v1 } = parseSignature(header ?? '') if (!t || v1.length === 0) return false const age = Math.abs(Math.floor(Date.now() / 1000) - Number(t)) if (!Number.isFinite(age) || age > TOLERANCE_SECONDS) return false const expected = crypto .createHmac('sha256', secret) .update(`${t}.`) .update(rawBody) // Buffer: the exact bytes received .digest() return v1.some((sig) => { const received = Buffer.from(sig, 'hex') return received.length === expected.length && crypto.timingSafeEqual(received, expected) }) } const app = express() // express.raw keeps the body as a Buffer; express.json() would break the signature. app.post('/hapana/webhooks', express.raw({ type: 'application/json' }), (req, res) => { if (!verifyHapanaSignature(req.body, req.get('X-Hapana-Signature'))) { return res.status(400).send('invalid signature') } const event = JSON.parse(req.body.toString('utf8')) enqueue(event) // process asynchronously res.sendStatus(200) }) ``` ## Python ```python import hashlib import hmac import os import time from flask import Flask, abort, request app = Flask(__name__) SECRET = os.environ["HAPANA_WEBHOOK_SECRET"] # whsec_... TOLERANCE_SECONDS = 300 def verify_hapana_signature(raw_body: bytes, header: str, secret: str = SECRET) -> bool: timestamp, signatures = None, [] for pair in header.split(","): key, _, value = pair.strip().partition("=") if key == "t": timestamp = value elif key == "v1": signatures.append(value) if not timestamp or not signatures: return False try: if abs(int(time.time()) - int(timestamp)) > TOLERANCE_SECONDS: return False except ValueError: return False signed_payload = timestamp.encode() + b"." + raw_body expected = hmac.new(secret.encode(), signed_payload, hashlib.sha256).hexdigest() return any(hmac.compare_digest(expected, sig) for sig in signatures) @app.post("/hapana/webhooks") def hapana_webhook(): raw = request.get_data() # verify the raw bytes before get_json() if not verify_hapana_signature(raw, request.headers.get("X-Hapana-Signature", "")): abort(400) enqueue(request.get_json()) # process asynchronously return "", 200 ``` ## Ruby ```ruby require 'json' require 'openssl' require 'rack/utils' require 'sinatra' SECRET = ENV.fetch('HAPANA_WEBHOOK_SECRET') # whsec_... TOLERANCE_SECONDS = 300 def verify_hapana_signature(raw_body, header, secret = SECRET) pairs = header.to_s.split(',').map { |pair| pair.strip.split('=', 2) } timestamp = pairs.find { |key, _| key == 't' }&.last signatures = pairs.select { |key, _| key == 'v1' }.map(&:last) return false if timestamp.nil? || signatures.empty? return false if (Time.now.to_i - Integer(timestamp, exception: false).to_i).abs > TOLERANCE_SECONDS expected = OpenSSL::HMAC.hexdigest('SHA256', secret, "#{timestamp}.#{raw_body}") signatures.any? { |sig| Rack::Utils.secure_compare(expected, sig) } end post '/hapana/webhooks' do raw = request.body.read halt 400, 'invalid signature' unless verify_hapana_signature(raw, request.env['HTTP_X_HAPANA_SIGNATURE']) enqueue(JSON.parse(raw)) # process asynchronously status 200 end ``` ## Go ```go package webhooks import ( "crypto/hmac" "crypto/sha256" "encoding/hex" "io" "net/http" "os" "strconv" "strings" "time" ) const toleranceSeconds = 300 var secret = []byte(os.Getenv("HAPANA_WEBHOOK_SECRET")) // whsec_... func VerifyHapanaSignature(rawBody []byte, header string, secret []byte) bool { var timestamp string var signatures []string for _, pair := range strings.Split(header, ",") { key, value, ok := strings.Cut(strings.TrimSpace(pair), "=") if !ok { continue } switch key { case "t": timestamp = value case "v1": signatures = append(signatures, value) } } if timestamp == "" || len(signatures) == 0 { return false } ts, err := strconv.ParseInt(timestamp, 10, 64) if err != nil { return false } if age := time.Now().Unix() - ts; age > toleranceSeconds || age < -toleranceSeconds { return false } mac := hmac.New(sha256.New, secret) mac.Write([]byte(timestamp + ".")) mac.Write(rawBody) expected := mac.Sum(nil) for _, sig := range signatures { received, err := hex.DecodeString(sig) if err == nil && hmac.Equal(received, expected) { return true } } return false } func Handler(w http.ResponseWriter, r *http.Request) { body, err := io.ReadAll(r.Body) if err != nil { http.Error(w, "read body", http.StatusBadRequest) return } if !VerifyHapanaSignature(body, r.Header.Get("X-Hapana-Signature"), secret) { http.Error(w, "invalid signature", http.StatusBadRequest) return } enqueue(body) // process asynchronously w.WriteHeader(http.StatusOK) } ``` ## PHP ```php TOLERANCE_SECONDS) { return false; } $expected = hash_hmac('sha256', $timestamp . '.' . $rawBody, $secret); foreach ($signatures as $signature) { if (hash_equals($expected, $signature)) { return true; } } return false; } $secret = getenv('HAPANA_WEBHOOK_SECRET'); // whsec_... $raw = file_get_contents('php://input'); $header = $_SERVER['HTTP_X_HAPANA_SIGNATURE'] ?? ''; if (!verify_hapana_signature($raw, $header, $secret)) { http_response_code(400); exit('invalid signature'); } enqueue(json_decode($raw, true)); // process asynchronously http_response_code(200); ``` ## Test vector With the clock check disabled (or the clock pinned to the timestamp), your verifier must accept: - Secret: `whsec_5f2b0c1d9e8a7b6c5d4e3f2a1b0c9d8e7f6a5b4c3d2e1f0a9b8c7d6e5f4a3b2c` - Timestamp: `1790982502` - Raw body (one line, no trailing newline): ``` {"id":"evt_7c1e1c52-3f0a-4a8e-9a43-2f5b1d0e6c11","object":"event","type":"booking.created","apiVersion":"2026-01-10","createdAt":"2026-10-02T23:08:22.481Z","livemode":true,"data":{"bookingId":"b3f1c2a4-5d6e-4f70-8a91-0b2c3d4e5f60"}} ``` - Expected: `X-Hapana-Signature: t=1790982502,v1=48373c7bd723348cf7520b12718567bdd0950ea5f913dbc30177a0287c732c27` ```sh printf '%s.%s' "1790982502" '{"id":"evt_7c1e1c52-3f0a-4a8e-9a43-2f5b1d0e6c11","object":"event","type":"booking.created","apiVersion":"2026-01-10","createdAt":"2026-10-02T23:08:22.481Z","livemode":true,"data":{"bookingId":"b3f1c2a4-5d6e-4f70-8a91-0b2c3d4e5f60"}}' \ | openssl dgst -sha256 -hmac "whsec_5f2b0c1d9e8a7b6c5d4e3f2a1b0c9d8e7f6a5b4c3d2e1f0a9b8c7d6e5f4a3b2c" ``` ## Common mistakes 1. **Verifying a re-serialised body.** Capture the raw bytes (`express.raw`, `request.get_data()`, `php://input`). 2. **Comparing the whole header.** The header is `t=,v1=`; parse out `v1`. 3. **Signing only the body.** The signed payload is `timestamp + "." + body`. 4. **Stripping the `whsec_` prefix.** The full secret string is the HMAC key. 5. **Using `==`.** Use `crypto.timingSafeEqual`, `hmac.compare_digest`, `Rack::Utils.secure_compare`, `hmac.Equal`, or `hash_equals`. 6. **Skipping the timestamp check.** Without it a captured delivery can be replayed. Keep your clock in sync (NTP). ## Secret rotation Rotating a webhook's secret in Core takes effect immediately; the old secret stops signing deliveries straight away. Each webhook has its own secret, so if several webhooks deliver to one endpoint, look the secret up per webhook (for example by a distinct URL path per webhook). The legacy v1 `checkIn` event is signed with the same headers and algorithm. # Webhook events: payload reference Every event a Hapana webhook can subscribe to, with the fields each one carries and an example body. Human-readable version: https://docs.hapana-developers.com/guides/webhooks/events/ | Event | Sent when | | -- | -- | | `member.created` | A member is created at a location. | | `member.updated` | A member's profile is edited. | | `member.suspended` | A member is blocked at a location. Unblocking sends `member.updated`. | | `member.deleted` | A member is archived. | | `membership.created` | A membership or package is purchased or moved to. | | `membership.activated` | A membership becomes usable for the first time: sold and paid for today, or a pre-sale reaching its start date. Sent once per membership per location; never for a resume, restore, extension or credit reactivation. | | `membership.updated` | A membership's status or details change, other than its first activation. | | `membership.cancelled` | A membership is cancelled. | | `membership.expired` | A membership expires. | | `membership.renewed` | A membership renews. | | `booking.created` | A member is booked into a session. | | `booking.cancelled` | A booking is cancelled or marked late-cancelled. | | `booking.completed` | A booking is marked attended. | | `booking.no_show` | A booking is marked as a no-show. | | `checkin.created` | A member checks in successfully. | | `session.created` | A session is scheduled. | | `session.updated` | A session is edited. | | `session.cancelled` | A session is cancelled. | | `staff.created` | A staff member is added. | | `staff.updated` | A staff member's profile is edited or they are reactivated. | | `staff.deactivated` | A staff member is deactivated. | | `checkIn` | Legacy v1 check-in body, for partners migrating from Hapana v1. | Every event except `checkIn` arrives in the standard envelope: ```json { "id": "evt_7c1e1c52-3f0a-4a8e-9a43-2f5b1d0e6c11", "object": "event", "type": "booking.created", "apiVersion": "2026-01-10", "createdAt": "2026-10-02T23:08:22.481Z", "livemode": true, "data": { } } ``` The `membership.*` events, `member.suspended` and `member.deleted` are being enabled location by location; until a location has them, a periodic reconcile keeps you current. Their payloads are deliberately thin (ids rather than the full record), so re-read the resource an event names. A member re-scanning into a class they have already attended deliberately emits nothing. The sections below describe `data`. Ids are Hapana ids (the same the Business API uses). Timestamps are ISO 8601 UTC. New fields may be added to `data` at any time; ignore fields you don't recognise. Headers on every delivery: `Content-Type: application/json`, `X-Hapana-Event-ID`, `X-Hapana-Event-Type`, `X-Hapana-Timestamp`, `X-Hapana-Signature` (`t=,v1=`), plus your custom header if set. See [verifying-signatures.md](./verifying-signatures.md). ## Member events ### `member.created` | Field | Type | Description | | -- | -- | -- | | `clientId` | string | The new member's id. | | `siteId` | string | The location the member joined. | | `firstName` | string \| null | First name. | | `lastName` | string \| null | Last name. | | `email` | string \| null | Email address. | ```json { "id": "evt_2b9c6f0e-41d7-4a55-8f0c-6a1e9b3d7c21", "object": "event", "type": "member.created", "apiVersion": "2026-01-10", "createdAt": "2026-10-02T21:14:05.112Z", "livemode": true, "data": { "clientId": "5a8d2e61-0c4b-4f3a-9e7d-1b2c3d4e5f6a", "siteId": "1f2e3d4c-5b6a-4978-8a9b-0c1d2e3f4a5b", "firstName": "Ada", "lastName": "Lovelace", "email": "ada@example.com" } } ``` ### `member.updated` Names the fields that changed, not their values. Read the member back through the API for the new values. | Field | Type | Description | | -- | -- | -- | | `clientId` | string | The member's id. | | `siteId` | string | The location the edit was made at. | | `changedFields` | string[] | Profile fields submitted in the edit, sorted. | ```json { "id": "evt_6d0e3b8a-9c5f-4b27-a1e4-0f8d2c6b9a13", "object": "event", "type": "member.updated", "apiVersion": "2026-01-10", "createdAt": "2026-10-02T21:20:41.903Z", "livemode": true, "data": { "clientId": "5a8d2e61-0c4b-4f3a-9e7d-1b2c3d4e5f6a", "siteId": "1f2e3d4c-5b6a-4978-8a9b-0c1d2e3f4a5b", "changedFields": ["email", "mobilePhoneNumber"] } } ``` ## Membership events ### `membership.activated` Sent once per membership per location the moment the membership becomes usable: a purchase paid for today, or a pre-sale reaching its start date (including a pre-sale that was migrated in). A door system can admit the member from this event alone. Memberships inserted by a bulk import or migration as already active send nothing. It is never sent for a resume from hold, a restore after cancellation, an extension or a credit reactivation; those arrive as `membership.updated`. Locations the member can use the membership at but does not belong to receive nothing. | Field | Type | Description | | -- | -- | -- | | `clientId` | string | The member's id. | | `siteId` | string | The location this delivery is for. | | `serviceId` | string | The membership id. | | `serviceSiteId` | string | The location that sold the membership. | | `serviceTemplateId` | string | The membership plan. | | `parentServiceId` | string \| null | The parent membership when this is an add-on. | | `barcode` | string \| null | The member's barcode at delivery time. | | `activatedAt` | string | When the membership became usable. | | `trigger` | string | `purchase`, `payment_activation`, `scheduled_activation`, `renewal` or `movement`. | ```json { "id": "evt_1b4f0c3d9a2e4c7f8e6d5a4b3c2d1e0f", "object": "event", "type": "membership.activated", "apiVersion": "2026-01-10", "createdAt": "2026-10-22T03:12:09.117Z", "livemode": true, "data": { "clientId": "5a8d2e61-0c4b-4f3a-9e7d-1b2c3d4e5f6a", "siteId": "1f2e3d4c-5b6a-4978-8a9b-0c1d2e3f4a5b", "serviceId": "9c7b6a5d-4e3f-4a2b-8c1d-0e9f8a7b6c5d", "serviceSiteId": "1f2e3d4c-5b6a-4978-8a9b-0c1d2e3f4a5b", "serviceTemplateId": "3e2d1c0b-9a8f-4e7d-6c5b-4a3f2e1d0c9b", "parentServiceId": null, "barcode": "8675309", "activatedAt": "2026-10-22T03:12:08.902Z", "trigger": "purchase" } } ``` The event id is derived from the membership and location, so a redelivery carries the same `id`; treat a repeated id as already handled. ## Booking events All booking events carry `bookingId`, `clientId`, `sessionId`, `siteId`, and `status` (the booking's new status: `PENDING`, `CONFIRMED`, `CANCELLED`, `LATE_CANCELLED`, `ATTENDED`, `NO_SHOW`, ...). ### `booking.created` Adds `bookingSource`: `CREDIT`, `FREE`, `DROP_IN`, `BILLING`, `ADMIN`, `AGGREGATOR`, `IMPORT`, or `GUEST_PASS`. ```json { "id": "evt_7c1e1c52-3f0a-4a8e-9a43-2f5b1d0e6c11", "object": "event", "type": "booking.created", "apiVersion": "2026-01-10", "createdAt": "2026-10-02T23:08:22.481Z", "livemode": true, "data": { "bookingId": "b3f1c2a4-5d6e-4f70-8a91-0b2c3d4e5f60", "clientId": "5a8d2e61-0c4b-4f3a-9e7d-1b2c3d4e5f6a", "sessionId": "9e0f1a2b-3c4d-4e5f-8a6b-7c8d9e0f1a2b", "siteId": "1f2e3d4c-5b6a-4978-8a9b-0c1d2e3f4a5b", "status": "CONFIRMED", "bookingSource": "CREDIT" } } ``` ### `booking.cancelled` Sent when a member or staff cancels (`status` `CANCELLED` or `LATE_CANCELLED`, plus `lateCancel` boolean), or when staff mark attendance as late-cancelled (`status` `LATE_CANCELLED`, no `lateCancel`). ```json { "id": "evt_a4f2d9c1-7e3b-4c80-b5a6-1d9e0f2c3b47", "object": "event", "type": "booking.cancelled", "apiVersion": "2026-01-10", "createdAt": "2026-10-03T06:42:10.027Z", "livemode": true, "data": { "bookingId": "b3f1c2a4-5d6e-4f70-8a91-0b2c3d4e5f60", "clientId": "5a8d2e61-0c4b-4f3a-9e7d-1b2c3d4e5f6a", "sessionId": "9e0f1a2b-3c4d-4e5f-8a6b-7c8d9e0f1a2b", "siteId": "1f2e3d4c-5b6a-4978-8a9b-0c1d2e3f4a5b", "status": "LATE_CANCELLED", "lateCancel": true } } ``` ### `booking.completed` and `booking.no_show` Staff marked the booking attended (`status` `ATTENDED`) or no-show (`status` `NO_SHOW`). Undoing an attendance mark (back to `CONFIRMED`) sends nothing. ```json { "id": "evt_e8b7c6d5-4a3f-4e21-9d0c-8b7a6f5e4d32", "object": "event", "type": "booking.completed", "apiVersion": "2026-01-10", "createdAt": "2026-10-03T08:05:51.660Z", "livemode": true, "data": { "bookingId": "b3f1c2a4-5d6e-4f70-8a91-0b2c3d4e5f60", "clientId": "5a8d2e61-0c4b-4f3a-9e7d-1b2c3d4e5f6a", "sessionId": "9e0f1a2b-3c4d-4e5f-8a6b-7c8d9e0f1a2b", "siteId": "1f2e3d4c-5b6a-4978-8a9b-0c1d2e3f4a5b", "status": "ATTENDED" } } ``` ## Check-in events ### `checkin.created` A member checked in successfully (barcode, fob, kiosk, app, contactless, or staff). Refused check-ins are not currently sent as `checkin.created`; `status` and `failReason` are included so handlers are ready if they are added. A re-scan inside the location's re-entry grace window sends nothing. | Field | Type | Description | | -- | -- | -- | | `checkInId` | string | The check-in id. | | `clientId` | string | The member. | | `siteId` | string | The location. | | `type` | string | `GENERAL`, `SESSION`, `TANNING`, or `ZONE`. | | `status` | string | `SUCCESS`, `FAIL`, or `UNKNOWN`. | | `failReason` | string \| null | e.g. `NO_VALID_MEMBERSHIP_OR_BOOKING`, `SUSPENDED`, `MEMBERSHIP_ON_HOLD`; `null` on success. | | `sessionId` | string \| null | The session, or `null` for a general check-in. | ```json { "id": "evt_0c9b8a7f-6e5d-4c3b-a2f1-e0d9c8b7a6f5", "object": "event", "type": "checkin.created", "apiVersion": "2026-01-10", "createdAt": "2026-10-02T23:08:22.517Z", "livemode": true, "data": { "checkInId": "d2c1b0a9-f8e7-4d6c-b5a4-93f2e1d0c9b8", "clientId": "5a8d2e61-0c4b-4f3a-9e7d-1b2c3d4e5f6a", "siteId": "1f2e3d4c-5b6a-4978-8a9b-0c1d2e3f4a5b", "type": "GENERAL", "status": "SUCCESS", "failReason": null, "sessionId": null } } ``` ## Session events `session.created`, `session.updated`, and `session.cancelled` carry the session as it is after the change: | Field | Type | Description | | -- | -- | -- | | `sessionId` | string | The session id. | | `siteId` | string | The location. | | `name` | string | Session name. | | `startTime` | string | ISO 8601 UTC. | | `endTime` | string | ISO 8601 UTC. | | `status` | string | `SCHEDULED`, `IN_PROGRESS`, `COMPLETED`, or `CANCELLED`. | | `scheduleTemplateId` | string or null | The schedule template the class was generated from (see `GET /sessions/templates`), or `null` for a class created on its own. | ```json { "id": "evt_3e4f5a6b-7c8d-4e9f-a0b1-c2d3e4f5a6b7", "object": "event", "type": "session.created", "apiVersion": "2026-01-10", "createdAt": "2026-10-01T02:11:37.204Z", "livemode": true, "data": { "sessionId": "9e0f1a2b-3c4d-4e5f-8a6b-7c8d9e0f1a2b", "siteId": "1f2e3d4c-5b6a-4978-8a9b-0c1d2e3f4a5b", "name": "Morning Yoga", "startTime": "2026-10-03T10:00:00.000Z", "endTime": "2026-10-03T11:00:00.000Z", "status": "SCHEDULED", "scheduleTemplateId": "5e550100-0001-4000-8000-000000000000" } } ``` ## Staff events One staff change produces **one event per location** the staff member can access, each with its own event id and `siteId`. Only locations your webhook covers are delivered. | Event | `data` | | -- | -- | | `staff.created` | `staffUserId`, `firstName`, `lastName`, `email`, `status` (`active`/`inactive`), `siteId` | | `staff.updated` | `staffUserId`, `siteId`; plus `status: "active"` when reactivated | | `staff.deactivated` | `staffUserId`, `status: "inactive"`, `siteId` | ```json { "id": "evt_4c5d6e7f-8a9b-4c0d-a1e2-f3a4b5c6d7e8", "object": "event", "type": "staff.created", "apiVersion": "2026-01-10", "createdAt": "2026-10-01T22:03:18.554Z", "livemode": true, "data": { "staffUserId": "c4d5e6f7-a8b9-4c0d-9e1f-2a3b4c5d6e7f", "firstName": "Jane", "lastName": "Smith", "email": "jane.smith@example.com", "status": "active", "siteId": "1f2e3d4c-5b6a-4978-8a9b-0c1d2e3f4a5b" } } ``` ## Legacy v1 check-in (`checkIn`) For partners that integrated with the Hapana v1 check-in webhook (for example Realtime Feedback). Reproduces the v1 body field for field, in the same order. - **No envelope**: the body is the bare v1 JSON object; top-level `type` is always `"checkIn"`. - **Same headers** as every other event (`X-Hapana-Event-ID`, `X-Hapana-Event-Type: checkIn`, `X-Hapana-Timestamp`, `X-Hapana-Signature`, custom header). v1 receivers can ignore them. - **Opt-in**: in its own Legacy group in Core, not included by "Select all events". New integrations should use `checkin.created`. - **Fires for refused check-ins too**, like v1. ```json { "uid": "17909825160599fd1f-fe1a-4ce6-9b41-3d7e00530401", "type": "checkIn", "siteID": "", "clientID": "", "checkInTime": "2026-10-02T23:08:22", "checkin_dt": "2026-10-02", "checkin_id": "", "firstName": "Brad", "lastName": "Shipman", "email": "member@example.com", "barcode": "0707", "fobNumber": "", "joinDate": "2026-07-23", "packageName": "Employee Membership", "packageID": "", "checkInStatus": "Valid", "checkInMessage": "Valid check-in - with alerts", "sessionType": "", "sessionName": "", "sessionID": "", "sessionStartTime": "", "sessionInstructor": "", "sessionInstructorID": "", "phone": "+15555550123", "customProperties": [] } ``` | Field | Description | | -- | -- | | `uid` | Unix seconds followed by the member's id. Extra bodies for additional sessions get a suffix. | | `type` | Always `checkIn`. | | `siteID` | Encrypted location id (the exact v1 value for a migrated location). | | `clientID` | Encrypted member id (the exact v1 value for a migrated member). | | `checkInTime` | UTC, `YYYY-MM-DDTHH:mm:ss`, no offset. | | `checkin_dt` | Date in the location's time zone, `YYYY-MM-DD`. | | `checkin_id` | Encrypted check-in id. | | `firstName`, `lastName`, `email`, `barcode`, `fobNumber` | Member record. | | `joinDate` | Join date in the location's time zone, `YYYY-MM-DD`. | | `packageName`, `packageID` | Membership/package the check-in used; id encrypted. | | `checkInStatus` | `Valid`, `Invalid`, or `error`. | | `checkInMessage` | v1 message string (table below). | | `sessionType`, `sessionName`, `sessionID` | Session type name, session name, encrypted session id. | | `sessionStartTime` | ISO 8601 with the location's UTC offset. | | `sessionInstructor`, `sessionInstructorID` | Comma-separated instructor names and encrypted ids. | | `phone` | Mobile, else home phone. | | `customProperties` | Filled custom properties: `[{ "fieldLabel": "...", "fieldValue": "..." }]`. | Empty values are `""`, never `null`; session fields are `""` for a general check-in. | Outcome | `checkInStatus` | `checkInMessage` | | -- | -- | -- | | Successful | `Valid` | `Valid check-in` | | Successful, failed or pending payments | `Valid` | `Valid check-in - with alerts` | | Refused: suspended | `Invalid` | `Invalid check-in - Suspended membership` | | Refused: on hold | `Invalid` | `Invalid check-in - with alerts - Membership on hold` | | Refused: any other reason | `Invalid` | `Invalid check-in - no valid membership or booking` | | Member not found | `error` | `no record found` | Success messages are capitalised for kiosk, Core, and widget check-ins and lowercase (`valid check-in`) for barcode, fob, contactless, QR, app, and other sources, as in v1. Compare case-insensitively. When one scan checks a member into several booked sessions, one body is sent per session (each its own delivery and event id), identical except for the session fields and a suffixed `uid`. A re-scan inside the re-entry grace window (15 minutes by default) sends nothing; a later re-scan sends a new body. Legacy ids are encrypted the same way v1 encrypted them, so ids stored from v1 keep matching. They are not the ids used by `checkin.created` or the Business API.