Creating and sharing an API key as a clinic admin
Mint an admin bearer API key from Practice Settings, pick the scopes your integration partner actually needs, capture the secret on the one screen that ever shows it, hand it off without leaking it, and set the rotation habit that keeps the whole thing safe. This is the workflow you run before a vendor writes a single line of code.
Audience
You run operations or administration at a clinic — at Clínica San Rafael, that's the admin who manages access for Doctora María Fernández's integration vendor. You are comfortable in Practice Settings and on a terminal for one verification command, but you are not the one building the integration. You decide which data leaves the clinic and who holds the key.
You'll need
- An admin-role user on your organization in HuliPractice. Only admins see the API Keys surface; a clinician or front-desk login does not.
- A short list from your integration partner of exactly which resources they read and write. You translate that list into scopes in step 2 — granting more than they need is the most common avoidable risk.
- A secure channel to hand the secret to the partner — a password manager share, an enterprise secrets vault, or an equivalent. Plain email and chat do not count.
curl(or any HTTP client) for the single verification request at the end. You can also hand the verification step to the partner.
End state
A named, active API key exists on your organization with the scopes your partner needs. You have captured its secret once, shared it through a secure channel, and confirmed it works with one 200 OK from a Patient search. You know how to rotate it and how to revoke it the moment it is no longer needed.
Steps
1. Open the API Keys surface
In HuliPractice, go to Practice Settings, open the Integrations section, and select API Keys. This page lists every key on your organization with its name, its scopes, its status, and when it was last used. If the section is absent, you are not signed in as an admin — switch to an admin login before continuing.
Select Create API key to start a new one.
2. Name the key and choose its scopes
Give the key a name that identifies the partner and the purpose, not the person who created it. A name like San Rafael — Lab results sync survives staff turnover; Maria's key does not. You will read this name later when you decide what to rotate or revoke, so make it self-explanatory.
Then select the scopes. Each scope is system/<Resource>.<perm>, where the permission letters are r (read), s (search), c (create), and u (update). Two combinations cover almost every integration:
- Read plus search — the
.rsform, e.g. system/Patient.rs. Grant this when the partner only pulls data out. - Full write — the
.cruform, e.g. system/Patient.cru. Grant this when the partner also creates and updates records. It includes read.
Which resources you can grant depends on whether they are writable in v1:
| Resource | Available scopes |
|---|---|
| Patient | system/Patient.rs · system/Patient.cru |
| Appointment | system/Appointment.rs · system/Appointment.cru |
| Encounter | system/Encounter.rs · system/Encounter.cru |
| Observation | system/Observation.rs · system/Observation.cru |
| Composition | system/Composition.rs · system/Composition.cru (BAA-gated) |
| DocumentReference | system/DocumentReference.rs · system/DocumentReference.cru (BAA-gated) |
| Practitioner | system/Practitioner.rs (read-only) |
| Organization | system/Organization.rs (read-only) |
| Provenance | system/Provenance.rs · system/Provenance.c |
Practitioner and Organization are read-only in v1, so only the .rs form exists. Provenance is the audit-trail resource a partner POSTs alongside a write; it offers read, search, and create (.rs and .c), and no update. (Encounters, observations, medication/service requests, compositions, and document references all sit under one BAA-gated "Clinical information" card; the webhook Subscription scopes are their own "Outbound webhooks" card — see the Scopes reference.)
3. Capture the secret — it is shown once
On confirmation, the surface displays the full secret token one time. This is the only moment the secret is ever visible — Practice Settings stores a hash, not the token, so it cannot show the value again on any later visit.
Copy the secret immediately and place it in your secrets manager before you leave or close the screen. If you navigate away without copying it, the key still exists but its secret is unrecoverable; your only path is to delete that key and mint a new one (steps 1–3).
After you have stored the secret, close the reveal. The list now shows the key as active, with its name and scopes, but never the secret again.
4. Share it with your integration partner
Hand the secret to the partner through the secure channel you prepared — a password manager share or a secrets vault, scoped to just the people who operate the integration. Send the partner three things:
- The secret token itself (through the secure channel, never inline in a message).
- The base URL,
https://api.huli.ai, with the FHIR R4 path under/fhir/R4/. - The list of scopes you granted, so the partner builds against exactly what the key allows and is not surprised by a 403 Forbidden on a resource you withheld.
The partner authenticates by sending the token as an HTTP Authorization: Bearer header on every request. They do not call the token endpoint and they do not need your private keys — the admin bearer token is the credential as-is.
5. Verify the key works
Run one authenticated request to confirm the key is live and correctly scoped. This example searches patients by name, so it needs a key with system/Patient.rs (or system/Patient.cru). You can run it yourself or hand it to the partner.
export HULI_API_KEY="<paste the secret you stored in step 3>"
curl "https://api.huli.ai/fhir/R4/Patient?name=Fern%C3%A1ndez&_count=20" \
-H "Authorization: Bearer $HULI_API_KEY" \
-H "Accept: application/fhir+json"
A working key returns 200 OK with a FHIR Bundle of type searchset. Encode the accent in the name (á becomes %C3%A1) so the search matches — this query looks up Doctora María Fernández's patients whose name starts with Fernández.
6. Rotate and revoke
Rotation replaces a key's secret without an outage: mint a new key (steps 1–3), share it with the partner (step 4), let them cut over and verify (step 5), then revoke the old one. Two keys are valid at once during the overlap, so nothing breaks mid-cutover. Rotate on a schedule — a calendar reminder per partner — and immediately if a secret is ever exposed.
To revoke, return to Practice Settings → Integrations → API Keys, find the key by the name you gave it in step 2, and revoke it. A revoked key stops authenticating right away; every request with it then returns 401 Unauthorized with HPB-00106 — see What can go wrong. Revoke a key the moment a partnership ends or a key is no longer in use — an unused active key is pure risk with no upside.
What to verify
- The new key appears in Practice Settings → Integrations → API Keys as active, with the name and scopes you intended.
- You captured the secret on the one-time reveal and stored it in a secrets manager — not in email, chat, or a repository.
- The verification request in step 5 returns 200 OK with a
Bundleof typesearchset. - The granted scopes match the partner's stated need and no more.
- You have a rotation reminder set and you know which list row to revoke when the partnership ends.
What can go wrong
The API returns errors as a FHIR OperationOutcome, not a bare string. The HTTP status and issue[0].code (the FHIR IssueType) classify the failure; the Huli code (HPB-…) is the prefix of issue[0].diagnostics, split on ": ". There is no details object and no text. These are the failures you will see while standing up a key:
HPB-00106 — auth failed. The token is missing, malformed, orrevoked. Confirm the header reads Authorization: Bearer <token> with a single space, that you pasted the full secret from the reveal screen, and that the key still shows as active in the list. After a revoke (step 6) this is the expected response for the old key.
HPB-00104 — insufficient scope. The token authenticated butlacks the scope the request needs — for the step 5 search, system/Patient.rs. The partner is calling a resource you did not grant. Re-mint the key with the right scopes, or confirm the partner is calling only what you granted.
400 Bad RequestHPB-00101 — validation error. A request parameter is malformed —most often an un-encoded accent in the name search. Encode á as %C3%A1.
HPB-00105 — rate limited. The key exceeded its request budget.The response carries a Retry-After header; wait that many seconds before retrying. A partner that trips this constantly is polling too aggressively — a workflow conversation, not a key problem.
A representative 403 body:
{
"resourceType": "OperationOutcome",
"issue": [
{
"severity": "error",
"code": "forbidden",
"diagnostics": "HPB-00104: Insufficient scope"
}
]
}
Next recipes
- Run your first authenticated Patient search — read the
searchsetBundle your verification request returns and recognize the four first-run failures in depth. - Authenticate as a SMART Backend Services client — when a partner needs short-lived, key-signed tokens (
client_credentials+private_key_jwt, RS384) instead of a long-lived admin bearer token. - Create and update a Patient — move a partner from system/Patient.rs to system/Patient.cru and POST/PUT patient records.
- Write an Observation with a Provenance trail — pairsystem/Observation.cru with system/Provenance.c soevery write carries its audit record.