# Authentication
The Hero Health Public API uses API key authentication on all requests. Every API key is tied to a specific Hero practice group and partner. Scopes control which endpoints your key can access.
## Getting your API key
### Step 1: Join the Partner Programme
Before you can access Hero's APIs, you need to become a verified partner:
1. Fill out our partner onboarding form at our [Get Started page](https://www.herohealthsoftware.net/partner-program/get-started)
2. Review and sign our Partner Agreement (includes SLA and rate limits)
3. Receive access credentials for both staging and production environments
### Step 2: Access developer resources
Once approved, you'll receive:
- A **staging API key** for development and testing
- A **practice group ID** for each practice you're integrating with
- Access to our Slack environment for developer support
### Step 3: Secure your credentials
Keep your keys secret
Never expose API keys in client-side code or commit them to source control. Store keys securely using environment variables or a secrets manager. Use separate keys for staging and production.
## Making authenticated requests
All requests require two headers:
| Header | Description |
| --- | --- |
| `x-api-key` | Your Hero Health API key |
| `x-practice-group-id` | The ID of the practice you are acting on behalf of |
```bash curl
curl -X GET "https://api.herohealth.net/v1/practice-groups" \
-H "x-api-key: YOUR_API_KEY" \
-H "x-practice-group-id: YOUR_PRACTICE_GROUP_ID"
```
```js Node.js
const response = await fetch('https://api.herohealth.net/v1/practice-groups', {
method: 'GET',
headers: {
'x-api-key': process.env.HERO_API_KEY,
'x-practice-group-id': process.env.HERO_PRACTICE_GROUP_ID,
},
});
if (!response.ok) {
throw new Error(`Request failed: ${response.status}`);
}
const data = await response.json();
```
```ruby Ruby
require 'net/http'
require 'json'
uri = URI('https://api.herohealth.net/v1/practice-groups')
http = Net::HTTP.new(uri.host, uri.port)
http.use_ssl = true
request = Net::HTTP::Get.new(uri)
request['x-api-key'] = ENV['HERO_API_KEY']
request['x-practice-group-id'] = ENV['HERO_PRACTICE_GROUP_ID']
response = http.request(request)
data = JSON.parse(response.body)
```
## Environments
| Environment | Base URL | Use for |
| --- | --- | --- |
| **Staging** | `https://api.staging.htech.app` | Development, testing, and integration verification |
| **Production** | `https://api.herohealth.net` | Live applications serving real practices |
Use test data only in staging — it does not contain real patient information.
## Cross-organisational calls
Cross-org access
Cross-organisational calls allow you to make API requests on behalf of a different practice group, provided both practices belong to the same Hero-configured network.
Hero supports cross-organisational API calls. If the `x-practice-group-id` header you pass does not match the practice group ID configured on your API key, Hero will verify that the target practice exists within a Hero-configured network alongside your key's practice. If such a network exists, the call returns data from the specified practice group.
The primary use case is cross-organisational bookings — for example, a practice within a PCN inviting patients to book at a hub site.
### Cross-organisational call flow
```mermaid
sequenceDiagram
participant Client as Your Server
participant Hero as Hero API
participant Network as Network Check
Client->>Hero: API request
x-api-key: YOUR_KEY
x-practice-group-id: target-practice
Hero->>Network: Verify practices share a network
alt Network exists
Network-->>Hero: Confirmed
Hero-->>Client: 200 OK { data from target practice }
else No network
Network-->>Hero: Denied
Hero-->>Client: 403 Forbidden
end
```
## Authentication errors
| Status | Error | Cause |
| --- | --- | --- |
| `401` | `invalid_credentials` | API key is missing, invalid, or expired |
| `403` | `insufficient_permissions` | Key does not have the required scope for this endpoint |
| `403` | `forbidden` | Cross-org access denied — practices are not in the same network |
| `429` | `rate_limit_exceeded` | Too many requests — implement retry with exponential backoff |
The API enforces a limit of **300 requests per minute** per API key. Contact [Hero support](mailto:support@herohealth.net) if your integration requires a higher limit.
## Security best practices
- Store API keys in environment variables or a secrets manager — never in source code
- Use separate keys for staging and production
- Request only the minimum scopes your integration needs
- All API calls must use HTTPS
- Contact Hero support to rotate a compromised key immediately
## Next steps
- [Basic concepts](/pages/api/get-started/basic-concepts) — understand Hero's data model
- [Your first API call](/pages/api/get-started/your-first-api-call) — make a working API request end-to-end