# After the patient connects

> Tie a finished session to your own user by storing the session ID when you create it and reading the subject when it completes.

The patient finishes on FinchNode's pages, not yours. Your server learns the result from FinchNode, never from the browser.

## Store the session when you create it

Save three values with your user before you send the patient to `url`:

| Value | Where it comes from |
| --- | --- |
| Your user's ID | Your own system |
| `sessionId` | The `id` in the create response |
| `externalId` | What you sent, if anything |

## Learn when sharing is approved

Only success is pushed. Use the webhook, the session, or both.

**The `consent.granted` webhook** arrives after the patient approves sharing. [Verify its signature](/docs/webhooks/receive-and-verify) first, then match its `data.sessionId` to the row you stored. This abbreviated, synthetic event shows the fields you'll use:

```json
{
  "id": "evt_9f2c4b1a0d7e6c5b3a21",
  "object": "event",
  "type": "consent.granted",
  "createdAt": "2026-09-30T18:04:12.000Z",
  "data": {
    "subject": "u_4f3a9c1e2b7d6a05",
    "receiptId": "rcpt_3F9A1C0B7E2D",
    "sessionId": "cs_4b2e9d0c7a1f5e3d8c6b",
    "externalId": "user_123",
    "environment": "sandbox",
    "categories": ["medications", "labs"],
    "requestedCategories": ["medications", "labs", "allergies"],
    "missingCategories": ["allergies"],
    "syncMode": "one-time"
  }
}
```

`missingCategories` lists what you asked for that this share doesn't include: the patient didn't approve it, or the source couldn't provide it. A patient who connects two health systems produces one `consent.granted` for each, with the same `sessionId`.

Webhooks are delivered in the background, so one can arrive a little after the session already reads `completed`.

**Reading the session** works without a webhook. `GET /connect/sessions/{sessionId}` returns `status: "completed"` and the `subject` once sharing is approved.

## Handle cancel, expiry, and partial imports

No webhook fires for these. Read the session from your server:

| You see | Means | Do this |
| --- | --- | --- |
| `canceled` | The patient or your server canceled. | Offer to try again with a new session. |
| `expired` | Nobody finished within 24 hours. | Create a new session when the user returns. |
| `pending` | They haven't started, or they left on the first screen. | It may still be open in their tab. Check again later. |
| `completed` with `sync.status: "partial"` | Some categories didn't arrive. | Use what arrived; `missingCategories` says what didn't. |

If nothing could be shared at all, the patient sees an error at the sharing step and the session doesn't complete.

## Don't trust the return URL

Arriving at your `returnUrl` proves nothing. A patient who cancels is sent there straight away, or to FinchNode's home page if you didn't set one. A patient who finishes sees a "Return to" button with your app's name. FinchNode adds nothing to the URL, and anyone can open it.

On your return page, look up the session you stored and read its status from your server.

## Never pick the first subject

`GET /users` returns a page of subjects who currently share with your app. Don't take the first one after a session completes; it may be someone else. Always use the `subject` from this session or its webhook.
