# Quickstart (https://docs.edflow.space/developers/quickstart)

Create a sandbox app, get an OAuth token, check health, send a heartbeat, an arrival and a triage event, watch the patient appear on the sandbox board, and receive your first webhook, in about 15 minutes.



This quickstart takes about 15 minutes. You'll create a sandbox app, authenticate with OAuth 2.0 client credentials, send three events and watch a fictional patient appear on the sandbox staff board. The sandbox holds synthetic data only and refuses anything that looks real.

## Before you start [#before-you-start]

* Access to the developer portal as an integration developer (ask your hospital's integration analyst or your EDFlow contact).
* `curl`, Node.js 20+ or Python 3.10+.
* The sandbox base URL: `https://api.sandbox.edflow.space`.

## 1. Create a sandbox app and a client secret [#1-create-a-sandbox-app-and-a-client-secret]

<Steps>
  <Step>
    ### Create the app [#create-the-app]

    In the developer portal, **Apps → New app**, environment **Sandbox**, preset **Integration engine** (scopes `events:write fhir:write hl7v2:write events:validate events:read`). Add `webhooks:read webhooks:write` for step 7.
  </Step>

  <Step>
    ### Create a client secret [#create-a-client-secret]

    **Credentials → Create client secret**. It is shown **once**: copy it, or download the `.env` file, and tick "I have stored it".
  </Step>
</Steps>

![Credentials and keys: each credential with its type, prefix, created date and last use.](https://docs.edflow.space/screenshots/web/dev-console-credentials.png)

```bash
export EDFLOW_API_BASE_URL="https://api.sandbox.edflow.space"
export EDFLOW_CLIENT_ID="edf_test_cli_…"
export EDFLOW_CLIENT_SECRET="edf_test_cs_…"
```

For production-style authentication with a private key instead of a secret, see [Authentication](/developers/authentication).

## 2. Get an access token [#2-get-an-access-token]

Tokens last 5 minutes. Cache one and refresh it 30 seconds before it expires, not on every request.

```bash
TOKEN=$(curl -sS "$EDFLOW_API_BASE_URL/oauth/token" \
  -u "$EDFLOW_CLIENT_ID:$EDFLOW_CLIENT_SECRET" \
  -d grant_type=client_credentials \
  -d "scope=events:write events:validate events:read" | jq -r .access_token)
```

## 3. Check health and find your site code [#3-check-health-and-find-your-site-code]

_getHealth: https://docs.edflow.space/openapi/edflow-v1.json_

```bash
curl -sS "$EDFLOW_API_BASE_URL/v1/sites" -H "Authorization: Bearer $TOKEN"
```

Your sandbox organization has one fictional site per real site, with a code ending in `-sbx`. Use it as `siteCode` below.

## 4. Send a heartbeat [#4-send-a-heartbeat]

A heartbeat every 60 s keeps the feed **healthy**. Without it, the site shows "feed delayed" after 5 minutes.

_ingestEventBatch: https://docs.edflow.space/openapi/edflow-v1.json_

## 5. Send an arrival and a triage [#5-send-an-arrival-and-a-triage]

Event ids must be deterministic and stable across retries; a retry with the same id is harmless. Patient identifiers in the sandbox must start with `SYN` and names must be synthetic (for example start with `ZZTEST`), or the event is refused as `phi_suspected`.

```bash
curl -sS "$EDFLOW_API_BASE_URL/v1/events/batch" \
  -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
  --data @- <<'JSON'
{ "events": [
  { "specVersion": "1.0", "id": "quickstart:ADT0001:0", "type": "encounter.arrived",
    "siteCode": "your-site-sbx", "occurredAt": "2026-09-14T08:02:11-04:00",
    "source": { "kind": "ehr", "system": "other", "feed": "adt", "messageType": "ADT^A04" },
    "encounter": { "externalId": "SYN3000001", "idSystem": "visit-number" },
    "data": { "arrivedAt": "2026-09-14T08:02:11-04:00", "arrivalMode": "walk_in",
      "patient": { "mrn": "SYN0000001", "givenName": "ZZTEST-Birch", "familyName": "ZZTEST-Sample",
        "birthDate": "1990-01-01", "administrativeGender": "unknown", "preferredLanguage": "en",
        "identityStatus": "confirmed", "privacy": { "confidential": false } },
      "location": { "code": "EDWR/WR01", "area": "waiting_room", "display": "Waiting room" } } },
  { "specVersion": "1.0", "id": "quickstart:ADT0002:0", "type": "encounter.triaged",
    "siteCode": "your-site-sbx", "occurredAt": "2026-09-14T08:15:30-04:00",
    "source": { "kind": "ehr", "system": "other", "feed": "adt", "messageType": "ADT^A08" },
    "encounter": { "externalId": "SYN3000001", "idSystem": "visit-number" },
    "data": { "triagedAt": "2026-09-14T08:15:30-04:00", "ctas": 3 } }
] }
JSON
```

The response has one result per event:

```json
{ "results": [
    { "id": "quickstart:ADT0001:0", "status": "accepted", "edflowEncounterId": "enc_…" },
    { "id": "quickstart:ADT0002:0", "status": "accepted" } ],
  "summary": { "accepted": 2, "duplicate": 0, "buffered": 0, "ignored": 0, "rejected": 0 } }
```

Timestamps are for the example; use recent ones (events more than 30 minutes old update state silently, and more than 90 days old are refused).

## 6. Watch it arrive [#6-watch-it-arrive]

* Sign in to the sandbox staff console with the sandbox staff account from your organization's welcome email. The patient appears on the board as "Waiting to be seen".
* The integration **Event log** shows both events as accepted.

![Live board: patients by zone with stage, time in department, assigned clinician, tests, open requests and bottleneck flags, with filters and saved views.](https://docs.edflow.space/screenshots/web/staff-board.png)

![Event log: each inbound event with type, status (accepted, duplicate, buffered, ignored or rejected) and time.](https://docs.edflow.space/screenshots/web/admin-integration-events.png)

## 7. Receive a webhook [#7-receive-a-webhook]

1. Expose an HTTPS endpoint (for example with a tunnel) and create a webhook endpoint in the portal for `request.created`. Copy the `whsec_…` signing secret.
2. Create a sandbox patient request: `POST /v1/sandbox/requests` with the encounter id.
3. Verify the delivery with a Standard Webhooks library. See [Webhooks](/developers/webhooks).

## Alternative: the signed `/ehr/v1` profile [#alternative-the-signed-ehrv1-profile]

Engines already built to the signed ingest profile send the same `{ "events": [...] }` body to `/ehr/v1/events`, signed with an HMAC source key instead of a bearer token. Sign and authenticate **at send time**, never in the transformer, so queued retries are re-signed. See [Authentication](/developers/authentication).

## Mirth Connect and Open Integration Engine [#mirth-connect-and-open-integration-engine]

Import the channel templates from [Engine configuration](/developers/engine-configuration). They fetch and cache the token in a JavaScript Writer destination at dispatch time, queue on `503` and `429`, and never hold the queue on `AE` or `AR`.

## Next steps [#next-steps]

* [Concepts](/developers/concepts): facts, stages, idempotency and ordering
* [HL7 v2](/developers/hl7v2) or [FHIR R4](/developers/fhir) if your engine sends those
* [Go live](/developers/go-live): conformance, shadow run and promotion
