Introduction

The Performance Hub Partner API enables external partner platforms to seamlessly integrate with Performance Hub's core modules. The API supports computer vision, analytics, member access logging, and facility insights.

Request Format

The API accepts JSON-encoded request bodies and returns JSON-encoded responses.

Rate Limiting

To ensure fair usage and system stability, Partner API accounts start with default limits:

  • 5 requests per second per endpoint
  • Recommended: 1000 items maximum per batch request for optimal performance
  • 10MB maximum payload size

Rate limits are adjustable and can be configured on a case-by-case basis by your Performance Hub partner account manager. If your integration needs higher throughput for launch, migrations, reporting backfills, or high-volume facilities, raise this during onboarding or before the planned change window.

Base URL

https://partner-api.performancehub.co/partner/v1/

Authentication

Access to the Partner API requires authentication using API secret keys. Generate and manage your keys through the Partner Console.

API keys serve as your credentials for accessing partner resources. Each key has restricted access to specific organisation data and can be deactivated instantly when needed.

Handle your API keys with care—they provide full access to your account! Never expose these keys in public repositories, client-side applications, or anywhere they could be compromised.

Authenticate requests using HTTP Basic Auth by passing your API key as the username. Leave the password empty.

The API only accepts secure HTTPS connections. Plain HTTP requests are rejected, as are any requests missing proper authentication credentials.

Example Request

curl https://partner-api.performancehub.co/partner/v1/organisations \
  -u sk_live_YOUR_PARTNER_SECRET_KEY:
# The colon prevents curl from asking for a password.

Verify Secret Key

curl https://partner-api.performancehub.co/partner/v1/auth/ping \
  -u sk_live_YOUR_PARTNER_SECRET_KEY:
# Returns { "message": "You are successfully authorised as <PartnerName>" }

Going-Live Checklist

Use this checklist before enabling a partner integration for production facilities.

Partner Console setup

  • Your partner profile is complete.
  • Your logo URL, website, category, and description are accurate.
  • Your data linking method is configured: Direct Link or Linking Identifier.
  • Facility linking has been tested with at least one real facility.
  • The right key type is being used:
    • Dryrun for non-destructive write testing.
    • Read-only for safe data consumption.
    • Live only when you are ready to affect production data.

Authentication and key management

  • Secret keys are stored server-side only.
  • Keys are not embedded in client-side applications, mobile apps, public repositories, or support tickets.
  • Key rotation and revocation have been tested.
  • Your team knows which key is used by each environment.

Data validation

  • Required fields are present for every endpoint you call.
  • Facility identifiers match your configured linking method.
  • IDs from your system are stable and do not change between syncs.
  • Large imports are split into batches under the documented limits.
  • Failed batch items can be identified and resent.

Endpoint testing

Before launch, test every endpoint your integration depends on:

  • Organisations and facilities can be read.
  • Members can be saved and updated.
  • Membership plans, access zones, and access hours sync correctly.
  • Check-ins are accepted and can be correlated back to your system.
  • Report snapshots use the expected reporting period and metrics.
  • Door/Gate Controller flows are tested on real hardware where applicable.

Access control readiness

For access control integrations:

  • Your system makes the access decision before calling Performance Hub.
  • The integration handles device_offline, maintenance_mode_active, and unlock_confirmation_timeout.
  • Operators understand that a timeout means the unlock was not confirmed.
  • Your logs store the returned event_id and command_id.
  • You have a manual fallback process for facility staff.

Error handling and retries

  • 400, 401, 403, 404, 409, and 422 responses are surfaced to operators or support teams rather than retried blindly.
  • 429, 500, 502, 503, and 504 responses use bounded retry logic with backoff.
  • Repeated failures create an alert in your system.
  • The integration never drops failed records silently.

Observability

  • Your logs include endpoint, timestamp, facility identifier, source record ID, request outcome, and response body.
  • Your support team can search by member ID, facility ID, check-in ID, or unlock event ID.
  • You have a way to replay or reconcile failed syncs.

Production launch

  • Performance Hub has confirmed your production access.
  • At least one production facility has enabled your integration.
  • Your production key is active.
  • Your operational and technical contacts are current.
  • Your support process is documented for customers.

After these checks are complete, your configured integration will appear in the Performance Hub partner directory inside the core Performance Hub platform.

Best Practices

Use these patterns to keep partner integrations predictable, observable, and safe for facilities.

Identify facilities consistently

Choose the linking method configured for your partner account and use it consistently:

  • Direct Link - use the Performance Hub facility_id returned by the facilities endpoint.
  • Linking Identifier - use your own location identifier, such as a branch ID, location code, or store number.

Do not send both identifiers in the same request. Performance Hub resolves requests using the linking method configured for your partner account.

See Partner Linking Methods for setup guidance.

Batch writes carefully

The Partner API accepts JSON request bodies and is designed for batch-oriented sync workflows where documented. Keep batches bounded and retryable.

Default guidance:

  • Use up to 1000 items per batch request for optimal performance.
  • Keep payloads under 10MB.
  • Stay within 5 requests per second per endpoint.

Rate limits are adjustable and can be configured on a case-by-case basis by your Performance Hub partner account manager. If you expect high-volume imports, launch traffic, or reporting backfills, agree the required throughput before the change window.

If you need to sync a large dataset, split it into deterministic chunks and log each chunk ID on your side. This makes failed retries easier to reason about.

Retry safely

Use exponential backoff for transient failures such as 429, 500, 502, 503, and 504.

Avoid tight retry loops. A good starting point is:

  1. Retry after 1 second.
  2. Retry after 2 seconds.
  3. Retry after 4 seconds.
  4. Stop and alert if the operation still fails.

For validation failures such as 400 or 422, fix the request before retrying.

Use idempotency where supported

The Door/Gate Controller unlock endpoint supports an optional idempotency_key. Reusing the same key for the same partner, facility, and door returns the existing result when one is available.

Use a stable key for the action being performed, for example a check-in ID or access event ID from your system.

Do not assume every write endpoint supports idempotency unless it is documented for that endpoint.

Keep source data clean

Send stable, normalized values wherever possible:

  • Use consistent member, plan, facility, and check-in IDs from your source system.
  • Use ISO 8601 timestamps for dates and times.
  • Send phone numbers in international format where available.
  • Send currencies using the fields documented for each endpoint.
  • Avoid overloading free-text fields with data that belongs in structured fields.

Design for incremental and full syncs

Most partner integrations need both:

  • Incremental syncs for day-to-day changes.
  • Full reconciliation syncs to correct missed events, historical imports, or data drift.

Your integration should be able to resend the current state for a member, membership plan, or report snapshot without depending on a previous request having succeeded.

Treat access control as safety-critical

For Door/Gate Controller unlocks:

  • Your system must decide whether the person is allowed to enter before calling Performance Hub.
  • Performance Hub returns 200 only after the on-site controller confirms the relay physically unlocked.
  • Most confirmations complete in 3-5 seconds.
  • A timeout is an explicit failed unlock, not a background success.
  • Retry only according to your own access and safety rules.

Always log the event_id and command_id returned by the unlock endpoint.

Monitor your integration

Use the Partner Console request logs during development and launch. In production, also maintain your own logs with:

  • Request timestamp.
  • Endpoint.
  • Source record ID.
  • Facility identifier.
  • HTTP status code.
  • Performance Hub response body or error message.

These fields make support and reconciliation much faster when a customer asks what happened.

Organisations

Represents the top-level business entity that operates one or more facilities integrated with the Performance Hub platform.

Endpoints

GET /partner/v1/organisations - List all organisations

The Organisation object

Attributes

  • org_id string required

    Unique identifier for the organisation.

  • name string required

    Organisation name.

The Organisation object

{
  "org_id": "6ba7b810-9dad-11d1-80b4-00c04fd430c8",
  "name": "FitLife Fitness Group"
}

List all organisations

Returns a list of organisations accessible to your partner account.

Returns

Returns a JSON object with a data property containing an array of Organisation objects.

Request

curl https://partner-api.performancehub.co/partner/v1/organisations \
  -u sk_live_YOUR_PARTNER_SECRET_KEY:

Response

{
  "data": [
    {
      "org_id": "6ba7b810-9dad-11d1-80b4-00c04fd430c8",
      "name": "FitLife Fitness Group"
    },
    {
      "org_id": "6ba7b811-9dad-11d1-80b4-00c04fd430c8",
      "name": "Wellness Studios Network"
    }
  ]
}

Facilities

Represents a physical location, branch, club, gym, or studio where members can access services.

Endpoints

GET /partner/v1/facilities - List all facilities

The Facility object

Attributes

  • facility_id string required

    Unique identifier for the facility.

  • org_id string required

    Unique identifier for the parent organisation.

  • name string required

    Facility name.

  • brand string optional

    Brand name used by the facility.

  • website_url string optional

    Facility website URL.

  • place_data object optional

    Location data from Google Places API containing country, formatted address, Google Place ID, province, and city.

  • social_links object optional

    Social media links for the facility.

  • timezone string optional

    IANA timezone identifier (e.g., "Australia/Sydney", "America/New_York").

The Facility object

{
  "facility_id": "550e8400-e29b-41d4-a716-446655440000",
  "org_id": "6ba7b810-9dad-11d1-80b4-00c04fd430c8",
  "name": "FitLife Downtown",
  "brand": "FitLife",
  "website_url": "https://fitlife.com/downtown",
  "social_links": {
    "instagram": {
      "handle": "@fitlifedowntown"
    },
    "facebook": {
      "handle": "FitLifeDowntown"
    }
  },
  "place_data": {
    "country": {
      "iso_code": "AU",
      "full_name": "Australia"
    },
    "formatted_address": "123 Main Street, Suite 100, Sydney NSW 2000, Australia",
    "google_place_id": "ChIJN1t_tDeuEmsRUsoyG83frY4",
    "province": {
      "abbreviation": "NSW",
      "full_name": "New South Wales"
    },
    "city": {
      "short_name": "Sydney",
      "full_name": "Sydney"
    }
  },
  "timezone": "Australia/Sydney"
}

List all facilities

Returns a list of facilities accessible to your partner account.

Returns

Returns a JSON object with a data property containing an array of Facility objects.

Request

curl https://partner-api.performancehub.co/partner/v1/facilities \
  -u sk_live_YOUR_PARTNER_SECRET_KEY:

Response

{
  "data": [
    {
      "facility_id": "550e8400-e29b-41d4-a716-446655440000",
      "org_id": "6ba7b810-9dad-11d1-80b4-00c04fd430c8",
      "name": "FitLife Downtown",
      "brand": "FitLife",
      "website_url": "https://fitlife.com/downtown",
      "place_data": {
        "country": {
          "iso_code": "AU",
          "full_name": "Australia"
        },
        "formatted_address": "123 Main Street, Sydney NSW 2000, Australia",
        "google_place_id": "ChIJN1t_tDeuEmsRUsoyG83frY4",
        "province": {
          "abbreviation": "NSW",
          "full_name": "New South Wales"
        },
        "city": {
          "short_name": "Sydney",
          "full_name": "Sydney"
        }
      },
      "timezone": "Australia/Sydney"
    }
  ]
}

Membership Plans

Defines a purchasable offering from an organisation or facility, including services, entitlements, schedule, and pricing.

Endpoints

POST /partner/v1/membership-plans - Save a membership plan

The Membership Plan object

Attributes

  • membership_plan_id string required

    Unique identifier for the membership plan.

  • facility_id string either/orProvide the facility identifier type configured for your integration: use facility_id for direct-link integrations, or linking_identifier for linking-identifier integrations. Do not send both; the API uses the identifier type configured for your partner account and ignores the other one. Learn more

    Unique identifier of the facility from Performance Hub.

  • linking_identifier string either/orProvide the facility identifier type configured for your integration: use facility_id for direct-link integrations, or linking_identifier for linking-identifier integrations. Do not send both; the API uses the identifier type configured for your partner account and ignores the other one. Learn more

    Unique identifier value for each location that the partner uses (e.g., "Branch ID", "Location Code", "Store Number"). Learn more

  • name string required

    Human-readable plan name (e.g., "Unlimited Monthly", "10 Class Pack").

  • billing_cycle string optional

    Billing frequency. One of weekly, fortnightly, monthly, annually, or one_time.

  • price integer optional

    Base cost in smallest currency unit (cents for USD/AUD).

  • currency string optional

    ISO 4217 currency code (e.g., "AUD", "USD").

  • description string optional

    Optional marketing or internal description.

  • active_members integer optional

    Number of active members using this membership plan

  • is_recurring boolean optional

    Whether the plan auto-renews.

  • is_trial boolean optional

    Whether this is a trial offering.

  • visible_to_public boolean optional

    Whether the plan is available for self-service purchase.

  • duration_days integer optional

    Number of days the membership plan is valid for.

  • access_zones array optional

    Array of access zones included in the membership plan. Each zone can define its own access hours. See Access Zones for details.

  • class_limit integer optional

    Maximum number of classes allowed (null for unlimited).

The Membership Plan object

{
  "membership_plan_id": "plan_99999",
  "facility_id": "550e8400-e29b-41d4-a716-446655440000",
  // "linking_identifier": "branch_downtown",  // If using linking identifier
  "name": "Unlimited Monthly",
  "description": "Unlimited access to all gym facilities and group classes",
  "billing_cycle": "monthly",
  "price": 8900,
  "currency": "AUD",
  "duration_days": 30,
  "is_recurring": true,
  "is_trial": false,
  "visible_to_public": true,
  "active_members": 245,
  "access_zones": [
    {
      "name": "Main Gym Floor",
      "description": "Full access to cardio and weight training equipment",
      "access_hours": {
        "mon": [["05:00", "22:00"]],
        "tue": [["05:00", "22:00"]],
        "wed": [["05:00", "22:00"]],
        "thu": [["05:00", "22:00"]],
        "fri": [["05:00", "22:00"]],
        "sat": [["06:00", "20:00"]],
        "sun": [["07:00", "18:00"]]
      }
    },
    {
      "name": "Group Fitness Studio",
      "description": "Access to group fitness classes",
      "access_hours": {
        "sat": [["06:00", "20:00"]],
        "sun": [["07:00", "18:00"]]
      }
    }
  ],
  "class_limit": null
}

Access Zones

Access zones define specific areas within a facility where a membership plan grants access. Each zone can have its own access hours, allowing different time restrictions per area.

Structure

Each access zone includes:

  • name string required

    The name of the zone (e.g., "Main Gym Floor", "Group Fitness Studio").

  • description string optional

    Description of what's included in that zone.

  • access_hours object optional

    Access hours specific to this zone. See Access Hours for the time range format.

Example

"access_zones": [
  {
    "name": "Main Gym Floor",
    "description": "Full access to cardio and weight training equipment",
    "access_hours": {
      "mon": [["05:00", "22:00"]],
      "tue": [["05:00", "22:00"]],
      "wed": [["05:00", "22:00"]],
      "thu": [["05:00", "22:00"]],
      "fri": [["05:00", "22:00"]],
      "sat": [["06:00", "20:00"]],
      "sun": [["07:00", "18:00"]]
    }
  },
  {
    "name": "Group Fitness Studio",
    "description": "Access to group fitness classes",
    "access_hours": {
      "sat": [["06:00", "20:00"]],
      "sun": [["07:00", "18:00"]]
    }
  }
]

Access Hours

Access hours specify when members can access a specific zone during each day of the week. Access hours are defined per access zone, allowing different time restrictions for different areas of a facility.

Structure

{
  "mon": [["start_time", "end_time"]],
  "tue": [["start_time", "end_time"]],
  "wed": [["start_time", "end_time"]],
  "thu": [["start_time", "end_time"]],
  "fri": [["start_time", "end_time"]],
  "sat": [["start_time", "end_time"]],
  "sun": [["start_time", "end_time"]]
}

Each day is an array of time ranges in HH:MM format (24-hour clock). An empty array [] means no access on that day. Days that are omitted are treated the same as an empty array.

Example Use Cases

// Early Bird Access
// Morning-only gym access, weekdays only:
"access_hours": {
  "mon": [["04:00", "12:00"]],
  "tue": [["04:00", "12:00"]],
  "wed": [["04:00", "12:00"]],
  "thu": [["04:00", "12:00"]],
  "fri": [["04:00", "12:00"]]
}
// Off-Peak Plan
// Avoids peak hours, weekdays only:
"access_hours": {
  "mon": [["10:00", "12:00"], ["14:00", "16:00"]],
  "tue": [["10:00", "12:00"], ["14:00", "16:00"]],
  "wed": [["10:00", "12:00"], ["14:00", "16:00"]],
  "thu": [["10:00", "12:00"], ["14:00", "16:00"]],
  "fri": [["10:00", "12:00"], ["14:00", "16:00"]]
}
// Weekend Warrior
// Full-day access on weekends:
"access_hours": {
  "sat": [["08:00", "20:00"]],
  "sun": [["08:00", "20:00"]]
}
// 24/7 Access (Most common use case)
// Always-on access, unlimited:
"access_hours": {
  "mon": [["00:00", "23:59"]],
  "tue": [["00:00", "23:59"]],
  "wed": [["00:00", "23:59"]],
  "thu": [["00:00", "23:59"]],
  "fri": [["00:00", "23:59"]],
  "sat": [["00:00", "23:59"]],
  "sun": [["00:00", "23:59"]]
}

Per-Zone Example

"access_zones": [
  {
    "name": "Main Gym Floor",
    "description": "Full access to cardio and weight training equipment",
    "access_hours": {
      "mon": [["05:00", "22:00"]],
      "tue": [["05:00", "22:00"]],
      "wed": [["05:00", "22:00"]],
      "thu": [["05:00", "22:00"]],
      "fri": [["05:00", "22:00"]],
      "sat": [["06:00", "20:00"]],
      "sun": [["07:00", "18:00"]]
    }
  },
  {
    "name": "Swimming Pool",
    "description": "Access to swimming pool and change rooms",
    "access_hours": {
      "mon": [["06:00", "20:00"]],
      "tue": [["06:00", "20:00"]],
      "wed": [["06:00", "20:00"]],
      "thu": [["06:00", "20:00"]],
      "fri": [["06:00", "20:00"]],
      "sat": [["08:00", "18:00"]],
      "sun": [["08:00", "18:00"]]
    }
  }
]

Save a membership plan

Creates or updates membership plan records. The API determines whether to create or update based on the membership_plan_id.

Parameters

  • membership_plans array required

    Array of Membership Plan objects to save. Recommended maximum: 1000 items per request for optimal performance. The API automatically batches items for processing. Each item must include either facility_id (required if not using linking_identifier) or linking_identifier (required if not using facility_id).

Returns

Returns a simple message confirming the sync operation.

Request

curl -X POST https://partner-api.performancehub.co/partner/v1/membership-plans \
  -u sk_live_YOUR_PARTNER_SECRET_KEY: \
  -H "Content-Type: application/json" \
  -d '{
    "membership_plans": [
      {
        "membership_plan_id": "plan_99999",
        "facility_id": "550e8400-e29b-41d4-a716-446655440000",
        // "linking_identifier": "branch_downtown",  // If using linking identifier
        "name": "Unlimited Monthly",
        "description": "Unlimited access to all gym facilities and group classes",
        "billing_cycle": "monthly",
        "price": 8900,
        "currency": "AUD",
        "duration_days": 30,
        "is_recurring": true,
        "is_trial": false,
        "visible_to_public": true,
        "active_members": 245,
        "access_zones": [
          {
            "name": "Main Gym Floor",
            "description": "Full access to cardio and weight training equipment",
            "access_hours": {
              "mon": [["05:00", "22:00"]],
              "tue": [["05:00", "22:00"]],
              "wed": [["05:00", "22:00"]],
              "thu": [["05:00", "22:00"]],
              "fri": [["05:00", "22:00"]],
              "sat": [["06:00", "20:00"]],
              "sun": [["07:00", "18:00"]]
            }
          },
          {
            "name": "Group Fitness Studio",
            "description": "Access to group fitness classes",
            "access_hours": {
              "sat": [["06:00", "20:00"]],
              "sun": [["07:00", "18:00"]]
            }
          }
        ],
        "class_limit": null
      }
    ]
  }'

Response

Returns a success message with metadata about the sync operation.

{
  "message": "Membership plan sync successful.",
  "metadata": {
    "success": 98,
    "skipped": 2,
    "skipped_items": [
      {
        "membership_plan_id": "plan_456",
        "facility_id": null,
        "reason": "Missing facility_id"
      }
    ]
  }
}

Metadata fields:

  • success - Number of membership plans successfully processed
  • skipped - Number of plans skipped (if any)
  • skipped_items - Array of skipped plans with reasons (if any)
  • dry-run - Boolean flag if using dryrun key type (if applicable)

Members

Represents an individual customer of a facility. Can be a lead, prospect, recurring customer, expired member, or blocked user.

Endpoints

POST /partner/v1/members - Save a member

The Member object

Attributes

  • member_id string required

    Unique identifier for the member.

  • facility_id string either/orProvide the facility identifier type configured for your integration: use facility_id for direct-link integrations, or linking_identifier for linking-identifier integrations. Do not send both; the API uses the identifier type configured for your partner account and ignores the other one. Learn more

    Unique identifier of the facility from Performance Hub.

  • linking_identifier string either/orProvide the facility identifier type configured for your integration: use facility_id for direct-link integrations, or linking_identifier for linking-identifier integrations. Do not send both; the API uses the identifier type configured for your partner account and ignores the other one. Learn more

    Unique identifier value for each location that the partner uses (e.g., "Branch ID", "Location Code", "Store Number"). Learn more

  • first_name string required

    Member's first name.

  • last_name string required

    Member's last name.

  • email string optional

    Email address.

  • mobile_phone string optional

    Mobile phone number in E.123 format (e.g., "+1 415 555 2671").

  • address object optional

    Physical address object with the following properties:

    • address_line_1 (string) - Primary street address
    • address_line_2 (string, optional) - Secondary address line
    • city (string) - City name
    • state (string) - State/province code
    • postal_code (string) - Postal/ZIP code
    • country (string) - ISO 3166-1 alpha-2 country code (e.g., "AU", "US")
    • country_name (string, optional) - Full country name
  • date_of_birth string optional

    Birth date in YYYY-MM-DD format.

  • gender string optional

    One of male, female, non_binary, prefer_not_to_say, or unknown.

  • type string optional

    Member type (e.g. staff, member, prospect).

  • tags array optional

    Quick helper strings or texts to categorise and identify specific members (e.g. ["vip", "personal-training", "student"]).

  • membership_status string optional

    Current membership status (e.g., active, expired, on_hold).

  • membership_plans array optional

    Array of membership plans associated with the member.

  • profile_picture string optional

    Base64-encoded profile picture (optional).

  • created string optional

    ISO 8601 timestamp when the member was created.

  • updated string optional

    ISO 8601 timestamp when the member was last updated.

The Member object

{
  "member_id": "member_11111",
  "facility_id": "550e8400-e29b-41d4-a716-446655440000",
  // "linking_identifier": "branch_downtown",  // If using linking identifier
  "first_name": "John",
  "last_name": "Smith",
  "email": "john.smith@email.com",
  "mobile_phone": "+61 412 345 678",
  "date_of_birth": "1985-06-15",
  "gender": "male",
  "type": "member",
  "tags": ["vip", "personal-training"],
  "profile_picture": "/9j/4AAQSkZJRgABAQAAAQ...",
  "membership_status": "current",
  "created": "2024-01-15T10:30:00Z",
  "updated": "2024-01-20T14:45:00Z",
  "address": {
    "address_line_1": "456 Bourke Street",
    "address_line_2": "Apt 12",
    "city": "Melbourne",
    "state": "VIC",
    "postal_code": "3000",
    "country": "AU",
    "country_name": "Australia"
  },
  "membership_plans": [
    {
      "membership_plan_id": "plan_99999",
      "name": "Unlimited Monthly",
      "start_date": "2024-01-01T00:00:00Z",
      "status": "current",
      "is_current": true,
      "end_date": null
    }
  ]
}

Save a member

Creates or updates member records. The API determines whether to create or update based on the member_id.

Parameters

  • members array required

    Array of Member objects to save. Recommended maximum: 1000 items per request for optimal performance. The API automatically batches items for processing. Each item must include either facility_id (required if not using linking_identifier) or linking_identifier (required if not using facility_id).

Returns

Returns a simple message confirming the sync operation.

Request

curl -X POST https://partner-api.performancehub.co/partner/v1/members \
  -u sk_live_YOUR_PARTNER_SECRET_KEY: \
  -H "Content-Type: application/json" \
  -d '{
    "members": [
      {
        "member_id": "member_11111",
        "facility_id": "550e8400-e29b-41d4-a716-446655440000",
        // "linking_identifier": "branch_downtown",  // If using linking identifier
        "first_name": "John",
        "last_name": "Smith",
        "email": "john.smith@email.com",
        "mobile_phone": "+61 412 345 678",
        "date_of_birth": "1985-06-15",
        "gender": "male",
        "type": "member",
        "tags": ["vip", "personal-training"],
        "profile_picture": "/9j/4AAQSkZJRgABAQAAAQ...",
        "membership_status": "current",
        "created": "2024-01-15T10:30:00Z",
        "updated": "2024-01-20T14:45:00Z",
        "address": {
          "address_line_1": "456 Bourke Street",
          "address_line_2": "Apt 12",
          "city": "Melbourne",
          "state": "VIC",
          "postal_code": "3000",
          "country": "AU",
          "country_name": "Australia"
        },
        "membership_plans": [
          {
            "membership_plan_id": "plan_99999",
            "name": "Unlimited Monthly",
            "start_date": "2024-01-01T00:00:00Z",
            "status": "current",
            "is_current": true,
            "end_date": null
          }
        ]
      }
    ]
  }'

Response

Returns a success message with metadata about the sync operation.

{
  "message": "Member sync successful.",
  "metadata": {
    "success": 245,
    "skipped": 5,
    "skipped_items": [
      {
        "member_id": "member_123",
        "facility_id": null,
        "reason": "Facility integration is disabled in facility-partner-config"
      }
    ]
  }
}

Metadata fields:

  • success - Number of members successfully processed
  • skipped - Number of members skipped (if any)
  • skipped_items - Array of skipped members with reasons (if any)
  • dry-run - Boolean flag if using dryrun key type (if applicable)

Check-Ins

Represents a record of a person being present at a facility, class, or activity. Used for tracking attendance, usage analytics, and improving visual detection accuracy.

Endpoints

POST /partner/v1/check-ins - Save check-ins

The Check-In object

Attributes

  • check_in_id string optional

    Unique identifier for the check-in. Optional - auto-generated from member_id and timestamp pair if not provided.

  • member_id string required

    Unique identifier of the member.

  • facility_id string either/orProvide the facility identifier type configured for your integration: use facility_id for direct-link integrations, or linking_identifier for linking-identifier integrations. Do not send both; the API uses the identifier type configured for your partner account and ignores the other one. Learn more

    Unique identifier of the facility from Performance Hub.

  • linking_identifier string either/orProvide the facility identifier type configured for your integration: use facility_id for direct-link integrations, or linking_identifier for linking-identifier integrations. Do not send both; the API uses the identifier type configured for your partner account and ignores the other one. Learn more

    Unique identifier value for each location that the partner uses (e.g., "Branch ID", "Location Code", "Store Number"). Learn more

  • check_in_type string optional

    Type of visit. One of member, class, staff, guest, or trial.

  • source string optional

    How the check-in was captured. Common values include manual, tag, rfid, kiosk, app. Additional values like door_reader or booking may also be used.

  • class_id string optional

    Optional. If tied to a class booking or attendance.

  • timestamp string required

    ISO 8601 format timestamp of when the check-in occurred.

The Check-In object

{
  // "check_in_id": "checkin_abc123",  // Optional - auto-generated from member_id + timestamp if not provided
  "member_id": "mem_12345",
  "facility_id": "550e8400-e29b-41d4-a716-446655440000",
  // "linking_identifier": "branch_downtown",  // If using linking identifier
  "check_in_type": "member",
  "source": "rfid",
  "timestamp": "2025-01-20T08:45:30Z"
}

Save check-ins

Creates check-in records. The API automatically deduplicates based on member, facility, and timestamp.

Parameters

  • check_ins array required

    Array of Check-In objects to save. Recommended maximum: 1000 items per request for optimal performance. The API automatically batches items for processing. Each item must include either facility_id (required if not using linking_identifier) or linking_identifier (required if not using facility_id).

Returns

Returns a simple message confirming the sync operation.

Request

curl -X POST https://partner-api.performancehub.co/partner/v1/check-ins \
  -u sk_live_YOUR_PARTNER_SECRET_KEY: \
  -H "Content-Type: application/json" \
  -d '{
    "check_ins": [
      {
        // "check_in_id": "checkin_abc123",  // Optional - auto-generated from member_id + timestamp if not provided
        "member_id": "mem_12345",
        "facility_id": "550e8400-e29b-41d4-a716-446655440000",
        // "linking_identifier": "branch_downtown",  // If using linking identifier
        "check_in_type": "member",
        "source": "rfid",
        "timestamp": "2025-01-20T08:45:30Z"
      }
    ]
  }'

Response

Returns a success message with metadata about the sync operation.

{
  "message": "Check-in sync successful.",
  "metadata": {
    "success": 523,
    "skipped": 3,
    "skipped_items": [
      {
        "member_id": "member_789",
        "facility_id": null,
        "reason": "No matching facility_id for the linking_identifier"
      }
    ]
  }
}

Metadata fields:

  • success - Number of check-ins successfully processed
  • skipped - Number of check-ins skipped (if any)
  • skipped_items - Array of skipped check-ins with reasons (if any)
  • dry-run - Boolean flag if using dryrun key type (if applicable)

Access Control

The Partner API can trigger Performance Hub Door/Gate Controller hardware for facilities where your integration is enabled.

Use this API when your member management system has already decided that a person is allowed to enter. Performance Hub does not evaluate membership status, access hours, door-zone rules, fraud checks, or suspension logic. Your system remains the authority for those decisions.

The unlock request is synchronous. Performance Hub sends the command to the on-site controller over MQTT and returns 200 only after the device confirms that the relay physically unlocked. Most confirmations should complete in 3-5 seconds. If no confirmation is received within the timeout window, the API returns an explicit failure.

Endpoints

GET /partner/v1/access-control/doors - List available Door/Gate Controllers
GET /partner/v1/access-control/doors/{door_id} - Get Door/Gate Controller status
POST /partner/v1/access-control/doors/{door_id}/unlock - Unlock a Door/Gate Controller

The Door/Gate Controller object

Attributes

  • door_id string required

    Unique identifier for the Door/Gate Controller. Store this in your system after discovery and use it when calling the unlock endpoint.

  • facility_id string required

    Performance Hub facility ID.

  • linking_identifier string optional

    Partner facility identifier when your integration uses linking identifiers. Learn more

  • name string required

    Human-readable device name configured in Performance Hub.

  • zone string optional

    Door zone or location, such as Entrance.

  • status string required

    online or offline.

  • relay_state string optional

    Latest known relay state, such as locked or unlocked.

  • maintenance_mode string required

    Current maintenance mode. One of off, force-open, or force-closed.

  • hold_time_seconds integer required

    Default unlock hold time configured for the door.

  • last_seen string optional

    ISO timestamp for the latest device metadata update.

The Door/Gate Controller object

{
  "door_id": "577393ce6ec584aa4e2e2b1d1e73934c",
  "facility_id": "550e8400-e29b-41d4-a716-446655440000",
  "linking_identifier": "branch_downtown",
  "name": "Front Door",
  "zone": "Entrance",
  "status": "online",
  "online": true,
  "relay_state": "locked",
  "maintenance_mode": "off",
  "hold_time_seconds": 10,
  "last_seen": "2026-05-01T04:30:00.000Z"
}

List Door/Gate Controllers

Returns Door/Gate Controllers available to your integration.

If neither facility_id nor linking_identifier is provided, the API returns Door/Gate Controllers across all facilities linked to your partner account.

Recommended: Include a facility identifier whenever possible. Requests without `facility_id` or `linking_identifier` may take longer to load for partners with hundreds or thousands of Door/Gate Controllers.

Parameters

  • facility_id string optional either/orProvide the facility identifier type configured for your integration: use facility_id for direct-link integrations, or linking_identifier for linking-identifier integrations. Do not send both; the API uses the identifier type configured for your partner account and ignores the other one. Learn more

    Performance Hub facility ID. Use this for direct-link integrations.

  • linking_identifier string optional either/orProvide the facility identifier type configured for your integration: use facility_id for direct-link integrations, or linking_identifier for linking-identifier integrations. Do not send both; the API uses the identifier type configured for your partner account and ignores the other one. Learn more

    Your own facility/location identifier. Use this when your integration is configured for linking identifiers.

Returns

Returns an array of Door/Gate Controller objects.

Request

curl -X GET "https://partner-api.performancehub.co/partner/v1/access-control/doors?facility_id=550e8400-e29b-41d4-a716-446655440000" \
  -u sk_live_YOUR_PARTNER_SECRET_KEY:

Response

{
  "data": [
    {
      "door_id": "577393ce6ec584aa4e2e2b1d1e73934c",
      "facility_id": "550e8400-e29b-41d4-a716-446655440000",
      "name": "Front Door",
      "zone": "Entrance",
      "status": "online",
      "relay_state": "locked",
      "maintenance_mode": "off",
      "hold_time_seconds": 10,
      "last_seen": "2026-05-01T04:30:00.000Z"
    }
  ]
}

Get Door/Gate Controller status

Retrieves the latest known state and basic configuration for a single Door/Gate Controller.

Provide a facility identifier so Performance Hub can verify that the door belongs to a facility your integration can access.

Parameters

  • door_id string required

    Door/Gate Controller ID returned by the list endpoint.

  • facility_id string either/orProvide the facility identifier type configured for your integration: use facility_id for direct-link integrations, or linking_identifier for linking-identifier integrations. Do not send both; the API uses the identifier type configured for your partner account and ignores the other one. Learn more

    Performance Hub facility ID. Required for direct-link integrations.

  • linking_identifier string either/orProvide the facility identifier type configured for your integration: use facility_id for direct-link integrations, or linking_identifier for linking-identifier integrations. Do not send both; the API uses the identifier type configured for your partner account and ignores the other one. Learn more

    Partner facility identifier. Required for linking-identifier integrations.

Returns

Returns a Door/Gate Controller object.

Request

curl -X GET "https://partner-api.performancehub.co/partner/v1/access-control/doors/577393ce6ec584aa4e2e2b1d1e73934c?facility_id=550e8400-e29b-41d4-a716-446655440000" \
  -u sk_live_YOUR_PARTNER_SECRET_KEY:

Response

{
  "data": {
    "door_id": "577393ce6ec584aa4e2e2b1d1e73934c",
    "facility_id": "550e8400-e29b-41d4-a716-446655440000",
    "name": "Front Door",
    "zone": "Entrance",
    "status": "online",
    "relay_state": "locked",
    "maintenance_mode": "off",
    "hold_time_seconds": 10,
    "last_seen": "2026-05-01T04:30:00.000Z"
  }
}

Unlock a Door/Gate Controller

Sends an unlock command to the Door/Gate Controller and waits for the physical relay to confirm it has unlocked.

Your system must decide whether the person is authorised before calling this endpoint. Performance Hub only verifies the partner key, facility access, door ownership, and device state.

Parameters

  • door_id string required

    Door/Gate Controller ID returned by the list endpoint.

  • facility_id string either/orProvide the facility identifier type configured for your integration: use facility_id for direct-link integrations, or linking_identifier for linking-identifier integrations. Do not send both; the API uses the identifier type configured for your partner account and ignores the other one. Learn more

    Performance Hub facility ID. Required for direct-link integrations.

  • linking_identifier string either/orProvide the facility identifier type configured for your integration: use facility_id for direct-link integrations, or linking_identifier for linking-identifier integrations. Do not send both; the API uses the identifier type configured for your partner account and ignores the other one. Learn more

    Partner facility identifier. Required for linking-identifier integrations.

  • member_id string required

    Partner member ID. This is used for audit logging and later data linking.

  • first_name string required

    First name of the person opening the door.

  • last_name string required

    Last name of the person opening the door.

  • full_name string optional

    Optional. If omitted, Performance Hub combines first_name and last_name.

  • check_in_id string optional

    Optional check-in ID from your system for correlation.

  • idempotency_key string optional

    Optional replay-protection key. Reusing the same key for the same partner, facility, and door returns the existing result when available.

Returns

Returns 200 only when the device confirms the relay physically unlocked. The door uses the hold time configured in Performance Hub Device Management; partners cannot override relay timing per request. Most confirmations should complete in 3-5 seconds. Treat the documented timeout as an explicit failed unlock and retry only according to your own safety rules.

Common failures:

  • 403 integration_not_enabled - your integration is not enabled for the facility.
  • 409 maintenance_mode_active - facility staff have enabled maintenance mode for the door.
  • 503 device_offline - the Door/Gate Controller is offline or stale.
  • 504 unlock_confirmation_timeout - the command was sent but no matching confirmation was received before timeout.

Request

curl -X POST https://partner-api.performancehub.co/partner/v1/access-control/doors/577393ce6ec584aa4e2e2b1d1e73934c/unlock \
  -u sk_live_YOUR_PARTNER_SECRET_KEY: \
  -H "Content-Type: application/json" \
  -d '{
    "facility_id": "550e8400-e29b-41d4-a716-446655440000",
    "member_id": "mem_12345",
    "first_name": "Jane",
    "last_name": "Smith",
    "full_name": "Jane Smith",
    "source": "door_reader",
    "idempotency_key": "unlock_20260501_0001"
  }'

Response

{
  "status": "unlocked",
  "door_id": "577393ce6ec584aa4e2e2b1d1e73934c",
  "facility_id": "550e8400-e29b-41d4-a716-446655440000",
  "event_id": "2d98e9ff-3a2a-46d4-a22f-5f23f7d75b11",
  "command_id": "9d4d013d-d929-4a87-8b52-6cc66f56ff07",
  "hold_time_seconds": 10,
  "confirmed_at": "2026-05-01T04:30:00.000Z"
}

Failure response

{
  "status": "failed",
  "reason": "device_offline",
  "door_id": "577393ce6ec584aa4e2e2b1d1e73934c",
  "facility_id": "550e8400-e29b-41d4-a716-446655440000",
  "event_id": "2d98e9ff-3a2a-46d4-a22f-5f23f7d75b11"
}

Facility Reports

A point-in-time record capturing key operational and financial metrics for a facility during a specific period.

Endpoints

POST /partner/v1/report-snapshots - Save report snapshots

The Report Snapshot object

Attributes

  • facility_id string either/orProvide the facility identifier type configured for your integration: use facility_id for direct-link integrations, or linking_identifier for linking-identifier integrations. Do not send both; the API uses the identifier type configured for your partner account and ignores the other one. Learn more

    Unique identifier of the facility from Performance Hub.

  • linking_identifier string either/orProvide the facility identifier type configured for your integration: use facility_id for direct-link integrations, or linking_identifier for linking-identifier integrations. Do not send both; the API uses the identifier type configured for your partner account and ignores the other one. Learn more

    Unique identifier value for each location that the partner uses (e.g., "Branch ID", "Location Code", "Store Number"). Learn more

  • start_date string required

    Snapshot period start date in YYYY-MM-DD format.

  • end_date string required

    Snapshot period end date in YYYY-MM-DD format.

  • metrics object required

    Metrics object containing member and revenue data for the period.

The Report Snapshot object

{
  "facility_id": "550e8400-e29b-41d4-a716-446655440000",
  // "linking_identifier": "branch_downtown",  // If using linking identifier
  "start_date": "2025-01-01",
  "end_date": "2025-01-31",
  "metrics": {
    "members": {
      "active": 1200,
      "new": 45,
      "cancelled": 12
    },
    "revenue": {
      "total_payments": 289000.00,
      "total_payments_count": 1200,
      "currency": "AUD"
    }
  }
}

Save report snapshots

Submits operational and financial metrics for a facility during a specific period.

Parameters

  • snapshots array required

    Array of Report Snapshot objects to save. Maximum 25 items per request. Each item must include either facility_id (required if not using linking_identifier) or linking_identifier (required if not using facility_id).

Returns

Returns a simple message confirming the sync operation.

Request

curl -X POST https://partner-api.performancehub.co/partner/v1/report-snapshots \
  -u sk_live_YOUR_PARTNER_SECRET_KEY: \
  -H "Content-Type: application/json" \
  -d '{
    "snapshots": [
      {
        "facility_id": "550e8400-e29b-41d4-a716-446655440000",
        // "linking_identifier": "branch_downtown",  // If using linking identifier
        "start_date": "2025-01-01",
        "end_date": "2025-01-31",
        "metrics": {
          "members": {
            "active": 1200,
            "new": 45,
            "cancelled": 12
          },
          "revenue": {
            "total_payments": 289000.00,
            "currency": "AUD"
          }
        }
      }
    ]
  }'

Response

{
  "message": "Report snapshot sync successful."
}

Errors

The Partner API uses standard HTTP status codes and JSON response bodies to explain why a request failed.

Error response shape

Most API errors return a JSON body with a message and, where available, structured details.

Status codes

Retry guidance

Retry these with bounded exponential backoff:

  • 429
  • 500
  • 502
  • 503
  • 504

Do not blindly retry these:

  • 400
  • 401
  • 403
  • 404
  • 409
  • 422

For access control, always treat timeout and offline responses as failed unlocks unless your own access rules and safety processes say otherwise.

Support details to include

When contacting support, include:

  • Endpoint and HTTP method.
  • Timestamp and timezone.
  • Partner key ID, not the secret key.
  • Facility identifier used.
  • Source record ID from your system.
  • Response status and body.
  • event_id or command_id where available.
{
  "success": false,
  "message": "Request body.members[0].member_id is required"
}

Some domain-specific endpoints return an explicit status and reason. For example, access-control failures include fields such as reason, door_id, facility_id, and event_id.

{
  "status": "failed",
  "reason": "device_offline",
  "door_id": "577393ce6ec584aa4e2e2b1d1e73934c",
  "facility_id": "550e8400-e29b-41d4-a716-446655440000",
  "event_id": "2d98e9ff-3a2a-46d4-a22f-5f23f7d75b11"
}

400 Bad Request

The request is malformed or missing required fields. Fix the request before retrying.

401 Unauthorized

Authentication failed. Check that you are using HTTP Basic Auth with your API secret key as the username and an empty password.

403 Forbidden

The key is valid but the operation is not allowed. Common causes include using a read-only key for a write operation or attempting to access a facility where your integration is not enabled.

404 Not Found

The requested resource was not found or is not available to your integration.

409 Conflict

The request conflicts with the current resource state. For access control, this can include a Door/Gate Controller being in maintenance mode.

422 Unprocessable Entity

The request is valid JSON but fails semantic validation. Fix the data before retrying.

429 Too Many Requests

The integration exceeded the documented rate limit. Retry with exponential backoff.

500 Internal Server Error

Performance Hub could not complete the request because of an internal error. Retry with backoff. If the problem continues, contact support with the endpoint, timestamp, and response body.

503 Service Unavailable

A dependent service or device is unavailable. For Door/Gate Controller unlocks, this can mean the controller is offline.

504 Gateway Timeout

The operation timed out. For Door/Gate Controller unlocks, this means the unlock was not confirmed before the timeout window.

Versioning

The Partner API is versioned in the URL. The current version is:

Change policy

Performance Hub aims to make additive, backward-compatible changes wherever possible.

Examples of backward-compatible changes:

  • Adding a new optional field to a request.
  • Adding a new field to a response.
  • Adding a new endpoint.
  • Adding a new enum value where clients are expected to handle unknown values.
  • Improving validation messages without changing the status code contract.

Examples of breaking changes:

  • Removing or renaming a field.
  • Changing the type of a field.
  • Making an optional request field required.
  • Removing an endpoint.
  • Changing the meaning of an existing status code or domain-specific reason.

Deprecations

When a breaking change is required, Performance Hub will avoid changing the existing version in place where possible. Instead, we will introduce a new version or migration path and communicate the change to affected partners.

Deprecation notices can include:

  • A changelog entry.
  • Direct partner communication.
  • Notes in the Partner Console.
  • Migration guidance in the docs.

Client recommendations

Build clients that tolerate additive changes:

  • Ignore response fields you do not use.
  • Treat unknown enum values as unsupported rather than crashing.
  • Avoid strict response equality checks.
  • Log unrecognized values so you can update your integration intentionally.

OpenAPI specifications

Use the published OpenAPI specifications to generate clients, validate requests, and track API shape changes over time.

See OpenAPI Specifications.

https://partner-api.performancehub.co/partner/v1/

Changelog

This changelog records notable Partner API and Partner Console changes that affect integration behaviour.

2026-05-06

2026-05-01

Change categories

Future entries use these categories:

  • Added - new endpoint, field, guide, or capability.
  • Changed - behaviour or documentation changed without removing support.
  • Deprecated - feature remains available but has a planned migration path.
  • Removed - feature or endpoint removed.
  • Fixed - bug fix or documentation correction.
  • Security - authentication, authorization, data handling, or key-management change.

Added

  • Added partner program documentation covering partner types, onboarding, best practices, go-live readiness, errors, versioning, and support.
  • Added a dedicated Become a Partner enquiry flow for prospective technical and commercial partners.
  • Added documentation for how approved partner integrations appear inside the Performance Hub partner directory in the core Performance Hub platform.

Added

  • Added Door/Gate Controller access-control documentation, including list, status, and unlock operations.
  • Added synchronous unlock behaviour documentation: the API returns success only after the on-site controller confirms the relay physically unlocked.
  • Added optional idempotency_key support documentation for Door/Gate Controller unlock requests.