# 00 · Authenticate

  Authenticate is the step where the rail learns which system is asking and where it is asking
  from. Every other stop is about a patient; this one is about your system.

## What this step is

Your system presents the credentials it was issued and gets back a token that says "this is a
known integrator". Nothing about a patient can happen until that token exists.

You establish two separate things here, and confusing them is the commonest way this stop
goes wrong.

<Wire
  rows={[
    { call: "Who you are", does: "A client id and secret exchanged for an access token. This is your identity as the vendor, and it does not change from one facility, patient, visit or day to the next.", keep: "access_token" },
    { call: "Which facility you are acting for", does: "Sent as headers on every call. You are one system serving many facilities, and almost every rule downstream (price, empanelment, which consent factors are offered) is scoped to this rather than to you.", keep: "facility code + its type" },
  ]}
/>

This matters more for an HMIS than it would for a single clinic's own software. Your token is
issued once to you, the vendor, and it does not change when the user switches site. The facility
does change, and it decides the answer. A correct token with the wrong or missing facility
authenticates perfectly and then fails three stops later, where the failure looks like something
else entirely: an empty benefit list for a member who is plainly covered, biometrics demanded at a
site that does not use them, a price that is wrong the same way every time.

Nothing in those symptoms mentions a facility, which is why this is a trap rather than an
ordinary mistake.

The rule that makes it work is that the token says who you are, the headers say where you are,
and the rail needs both on every call.

There are three things to do.

<BuildSequence
  steps={[
    {
      do: "Exchange credentials for a token",
      detail:
        "One call with your client id and secret. You get back an access token and the number of seconds it is good for.",
    },
    {
      do: "Derive the facility headers, do not configure them",
      detail:
        "Have one place in your HTTP client read the facility from whichever site the session belongs to, so no call site can omit it and no call can carry the wrong one. A hard-coded facility code works in a single-site pilot and then silently misprices everything the day a second site goes live.",
    },
    {
      do: "Decide where decisions get delivered",
      detail:
        "Preauthorisation and claim outcomes come back asynchronously, so the rail needs somewhere to send them. Register an endpoint here and every call uses it, or pass one per request in the X-Callback-Url header where a particular call needs its own. Settle this at 00 rather than finding out about it at Submit.",
    },
  ]}
/>

If you only remember one thing: <u>the facility headers are as required as the token, and both
belong in the HTTP client rather than at the call site.</u>

Two ways to look at the step: who answers whom, and what happens to the token over its life.

<Mermaid chart={`sequenceDiagram
    autonumber
    participant H as Your HMIS
    participant I as Identity service
    participant R as The rail

    H->>I: client_id + client_secret
    I-->>H: access_token, expires_in
    Note over H: Cache the token.<br/>Refresh before expiry, on a timer.
    H->>R: Any call + Bearer token + facility headers
    R-->>H: Answer, scoped to that facility
    Note over H,R: The secret is used once, here.<br/>Everything after it carries the token.
`} />

<Mermaid chart={`flowchart TD
    A["A call is about to be made"] --> B{Token cached and still fresh?}
    B -- Yes --> C["Send it, with the facility headers"]
    B -- "Expiring soon" --> D["Refresh on the timer,<br/>then send"]
    D --> C
    B -- "Already expired" --> E["401 on a patient-facing call,<br/>the case you can avoid"]
    E --> F["Refresh and retry once,<br/>then fix the timer"]
    C --> G{Facility headers present?}
    G -- No --> H["FACILITY_CONTEXT_MISSING<br/>or a strict-default refusal later"]
    G -- Yes --> I["Answer, scoped to the facility"]
`} />

## The call

Two calls make up this step. One exchanges credentials for a token. The other registers where
decisions should be delivered. Both are in the reference with every parameter, and with a **Try it** console for when the sandbox is live.

<Wire
  rows={[
    { call: "POST · token", does: "Exchanges your client id and secret for an access token, with the seconds it is valid for.", keep: "access_token, expires_in" },
    { call: "POST · callbacks", does: "Registers the endpoint that preauth, claim and remittance decisions are delivered to.", keep: "callback_id" },
  ]}
/>

Open [**00 · Authenticate in the Rail API reference**](/rail-api/00-authenticate) to read or run
both. They are the whole of this stop, and there is no wider authentication surface to go and
find: one grant type (`client_credentials`), tokens good for thirty minutes, and no refresh
token, so you request a new token on expiry rather than refreshing the old one.

  It is the one call in this documentation not made against the rail's gateway. The identity
  service issues the exchange on its own host, it is form-encoded rather than JSON, and it
  carries no bearer token of its own, because the credentials are the authentication. Everything
  after it goes to the rail with the token attached.

  The same operation is also published on its own, because the eTIMS branch needs it without the
  rail around it. There is one description rather than two copies: the rail's spec lifts that
  operation from the authentication spec when it is generated, and the build fails if the two ever
  stop matching.

Two things the reference will not tell you.

<Wire
  rows={[
    { call: "Cache it", does: "The reference shows one exchange. It does not tell you that fetching a token per request will get you throttled and make every call slow.", keep: "access_token + expiry" },
    { call: "Send the facility every time", does: "The reference lists the headers. Only this page tells you that leaving them out fails at Consent and the cover check rather than here.", keep: "facility headers" },
  ]}
/>

## Facility context travels in headers

They never travel in a request body, and you should not assemble them at each call site.

<Wire
  rows={[
    { call: "Authorization: Bearer …", does: "Identifies your system", keep: "access_token" },
    { call: "X-Facility-Id", does: "Identifies the facility the call is made from", keep: "facility code" },
    { call: "X-Facility-Id-Type", does: "Says which identifier scheme that code is in", keep: "id type" },
    { call: "X-Callback-Url", does: "Optional. Where this request's decision should go, if it should not go to your registered endpoint.", keep: "the URL you sent" },
  ]}
/>

Two of those three are required on every call. The callback header is the exception, and you send
it only when this request's outcome belongs somewhere other than the endpoint you registered.

If a facility identifier appears in a request body anywhere in your codebase, treat it as a bug
that will surface on a busy day. The header is the only channel the rail guarantees to honour.

  You exchange the client secret once, from a server you control, and store it in a secret
  manager. The token is what travels. Never log the token, never put it in a URL, and never
  ship the secret to a browser or a mobile client. Anyone holding a bearer credential can act
  as you.

## What you keep

<Wire
  rows={[
    { call: "access_token", does: "Sent on every subsequent call, at every stop", keep: "In memory, with its expiry" },
    { call: "expires_in", does: "What your refresh timer is set from. Log it, because a 401 is hard to explain if you cannot say when the token was due to run out.", keep: "Computed expiry" },
    { call: "Facility code and type", does: "Headers on every call. They scope pricing, empanelment and which consent factors are offered, so they belong to the session rather than to your deployment.", keep: "Resolved per session" },
    { call: "callback_id", does: "Proof a delivery endpoint is registered. Keep it even if you also send per-request callbacks, since the registered endpoint is the fallback for everything that does not carry a header.", keep: "Your integration record" },
  ]}
/>

## What breaks here

Almost nothing breaks *at* this stop, which is what makes it expensive. The failures surface
two or three stops away, and they do not look like authentication problems when they do.

<CostTable
  rows={[
    {
      where: "Every call, suddenly",
      symptom: "\"Everything returns 401 since this morning\"",
      cause:
        "The token expired and refresh is reactive or absent. Cache with a proactive timer, and log the expiry you computed.",
    },
    {
      where: "Under load",
      symptom: "\"Calls got slow, then we started getting throttled\"",
      cause:
        "A token fetched per request. One exchange should serve thousands of calls.",
    },
    {
      where: "Consent",
      symptom: "\"Biometrics is being demanded and we are not set up for it\"",
      cause:
        "Facility context missing or wrong, so the facility's authentication policy resolved to the strict default.",
    },
    {
      where: "Eligibility",
      symptom: "\"The benefit list is empty for a patient who is definitely covered\"",
      cause:
        "Benefits are filtered by facility empanelment. The wrong facility gives you an empty list, and nothing in the error mentions facilities.",
    },
    {
      where: "Bill",
      symptom: "\"The price is wrong, and it is wrong consistently\"",
      cause:
        "Tariff variants are facility-scoped. A stale or hard-coded facility code prices every line against the wrong contract.",
    },
    {
      where: "Submit",
      symptom: "\"The claim will not send and we cannot see why\"",
      cause:
        "No delivery endpoint at all, neither a registered callback nor an X-Callback-Url header on the request. Submission will not proceed without somewhere to send the decision.",
    },
    {
      where: "Three weeks after go-live",
      symptom: "\"We are live and no money has arrived\"",
      cause:
        "The banking record was never completed. A human reviews it, and it gates every payment regardless of how many claims were accepted.",
    },
  ]}
/>

One pattern accounts for most of that list. Credentials get treated as setup and are then never
looked at again: a token with no logged expiry, a facility code nobody re-reads, a callback
registered in one environment. None of the three shows up until a patient is waiting.

### Errors

<ErrorDictionary stop="authenticate" />

## What carries forward

<Wire
  rows={[
    { call: "access_token", does: "Every stop from 01 to 08 takes it as a header. Nothing on the rail works without it.", keep: "Refresh before it dies" },
    { call: "Facility headers", does: "They scope every answer you get. The same call from two facilities gives you two different answers.", keep: "Set once in the client" },
    { call: "A place to deliver decisions", does: "Checked at 06 · Preauthorize and 08 · Submit. Register an endpoint here and the check is invisible; override it per request with X-Callback-Url where you need to.", keep: "Per environment" },
  ]}
/>

There is no `next_action` at this stop, because nothing patient-shaped has happened yet. You
are just ready to begin.

**Next:** [01 · Identify](/rail/stops/Identify). With a token and a facility, you can resolve
who is in front of you.

<AgentPrompt title="Build Authenticate with an agent" filename="authenticate.instructions.md">{`You are integrating an HMIS with the Savannah unified payer rail.

Task: implement stop 00, Authenticate. This stop establishes two things: who your
system is, and which facility it is calling from.

TOKEN
  Exchange the client id and secret for an access token, server-side only.
  Read expires_in from the response and compute an absolute expiry.
  Cache the token. Refresh proactively on a timer at roughly 80% of its life.
  Do NOT refresh reactively on a 401: that turns one slow call into two while a
  patient is waiting.
  Never log the token, never place it in a URL, never ship the secret to a
  browser or mobile client.

FACILITY CONTEXT
  You are one system acting for many facilities. The token identifies you as the
  vendor; the facility headers say which site the current work belongs to, and
  they decide almost every downstream rule.
  Configure one HTTP client that attaches on every request:
    Authorization: Bearer <token>
    X-Facility-Id: <facility code>
    X-Facility-Id-Type: <scheme>
  Resolve the facility from the session or tenant rather than from configuration.
  A hard-coded code survives a single-site pilot and then misprices everything
  once a second site is live.
  Set these in the client, not at call sites. A facility identifier must never
  appear in a request body.
  Treat facility context as required on every call, not only the first.

CALLBACKS
  Preauth and claim decisions are asynchronous, so the rail needs a delivery
  target. There are two routes and they compose:
    - Register an endpoint for preauth.decision, claim.decision and
      remittance.posted, and persist the returned callback_id. This is the
      default for every call that says nothing.
    - Send X-Callback-Url: <url> on an individual request to have that
      request's decision delivered elsewhere.
  Register the default before building later stops: submission will not proceed
  with no delivery target at all.

OPERATIONAL RULES
  - Log the computed token expiry, or a 401 will be impossible to account for
    later.
  - One token exchange should serve thousands of calls. Per-request exchange
    will be throttled.
  - Register callbacks per environment; a sandbox registration does not carry
    to production. The same applies to any URL you send in X-Callback-Url.
  - Complete the banking record early. It is human-reviewed and gates payment
    no matter how many claims are accepted.

WHAT FAILS ELSEWHERE IF THIS IS WRONG
  Missing or wrong facility context does not fail here. It surfaces as an empty
  benefit list at the cover check, as unexpected biometrics at Consent, and as
  mispriced lines at Bill.`}</AgentPrompt>
