# Your first API call This guide walks you through making your first successful API call — registering a patient via NHS PDS (Personal Demographics Service). PDS tracing is at the heart of Hero's patient communication and booking flows: it ensures you're working with the most up-to-date contact details for a patient before sending a message or generating a booking link. ## Prerequisites Before you begin: - You have joined the [Hero Partner Programme](https://www.herohealthsoftware.net/partner-program/get-started) and received your API key and practice group ID - You have read the [Authentication guide](/pages/api/authentication) ## Step 1: Store your credentials Store your API key and practice group ID in environment variables. Never hard-code credentials in source files. ```bash # .env HERO_API_KEY=your_staging_or_production_key_here HERO_PRACTICE_GROUP_ID=your_practice_group_id_here ``` Start on staging Use your staging API key and `https://api.staging.htech.app` as the base URL while building and testing your integration. Switch to `https://api.herohealth.net` and your production key when you go live. ## Step 2: Register a patient via PDS `POST /v1/patients/register_pds` traces a patient against the NHS Spine using their NHS number and date of birth. Hero returns a `patient_id` you can use in subsequent messaging and booking calls. ```bash curl curl -X POST "https://api.staging.htech.app/v1/patients/register_pds" \ -H "x-api-key: YOUR_API_KEY" \ -H "x-practice-group-id: YOUR_PRACTICE_GROUP_ID" \ -H "Content-Type: application/json" \ -d '{ "nhs_number": "123 456 7891", "dob": "1992-01-01" }' ``` ```js Node.js const response = await fetch('https://api.staging.htech.app/v1/patients/register_pds', { method: 'POST', headers: { 'x-api-key': process.env.HERO_API_KEY, 'x-practice-group-id': process.env.HERO_PRACTICE_GROUP_ID, 'Content-Type': 'application/json', }, body: JSON.stringify({ nhs_number: '123 456 7891', dob: '1992-01-01', }), }); if (!response.ok) { throw new Error(`Request failed: ${response.status}`); } const data = await response.json(); console.log(data); ``` ```ruby Ruby require 'net/http' require 'json' uri = URI('https://api.staging.htech.app/v1/patients/register_pds') http = Net::HTTP.new(uri.host, uri.port) http.use_ssl = true request = Net::HTTP::Post.new(uri) request['x-api-key'] = ENV['HERO_API_KEY'] request['x-practice-group-id'] = ENV['HERO_PRACTICE_GROUP_ID'] request['Content-Type'] = 'application/json' request.body = JSON.generate({ nhs_number: '123 456 7891', dob: '1992-01-01' }) response = http.request(request) data = JSON.parse(response.body) puts data ``` ## Step 3: Understand the response There are two possible success responses depending on whether the patient needs to verify their identity. ### Patient registered successfully ```json { "patient_id": "98765" } ``` Store the `patient_id` — you'll pass it to subsequent API calls such as generating a booking link or sending a message. ### Patient identity verification required ```json { "message": "Patient must verify their identity before proceeding", "login_url": "https://patient.herohealth.net/verify/abc123" } ``` This occurs when Hero cannot fully confirm the patient's identity from PDS alone. Direct the patient to the `login_url` to complete verification before retrying. ## Handling errors | Status | Cause | Fix | | --- | --- | --- | | `400 Bad Request` | Missing or invalid `nhs_number` or `dob` | Check the request body matches the expected format | | `401 Unauthorized` | Missing or invalid `x-api-key` | Verify your key and that you're pointing at the right environment | | `403 Forbidden` | Key does not have access to this practice | Check your `x-practice-group-id` matches your key's configured practice | | `404 Not Found` | No patient found on PDS for the given NHS number and DOB | Confirm the details are correct and the patient is registered with NHS | | `429 Too Many Requests` | Rate limit exceeded | Retry with exponential backoff using the `Retry-After` header | ### Retry with backoff ```js Node.js async function postWithRetry(url, options, retries = 3) { const response = await fetch(url, options); if (response.status === 429 && retries > 0) { const retryAfter = parseInt(response.headers.get('Retry-After') || '60', 10); await new Promise(resolve => setTimeout(resolve, retryAfter * 1000)); return postWithRetry(url, options, retries - 1); } if (!response.ok) { throw new Error(`Request failed: ${response.status}`); } return response.json(); } ``` ```ruby Ruby def post_with_retry(uri, request, retries: 3) http = Net::HTTP.new(uri.host, uri.port) http.use_ssl = true response = http.request(request) if response.code == '429' && retries > 0 retry_after = (response['Retry-After'] || '60').to_i sleep(retry_after) return post_with_retry(uri, request, retries: retries - 1) end raise "Request failed: #{response.code}" unless response.is_a?(Net::HTTPSuccess) JSON.parse(response.body) end ``` ## Next steps With a `patient_id` in hand, you can now: - [Generate a booking link](/pages/api/booking-links/overview) — invite the patient to book an appointment - [Send a message](/pages/api/messaging/overview) — send the patient a communication - [Basic concepts](/pages/api/get-started/basic-concepts) — understand Hero's full data model - [API Reference](/apis/public-api/openapi) — explore all available endpoints