---
title: 'Subscription (FHIR R4)'
description: 'FHIR R4 Subscription resource.'
nav: 'API / R4 / Subscription'
order: 50
version: v1
fhir: r4
source: generated
updated: '2026-09-23'
---

# Subscription (FHIR R4)

<FhirResourceHeader fhir="r4" slug="subscription" />

FHIR R4 Subscription resource.

> This reference is generated from the canonical Huli Public API OpenAPI specification and the server's FHIR R4 CapabilityStatement — do not edit it directly.

## FHIR R4 Specification

Official spec: [Subscription — HL7 FHIR R4](https://hl7.org/fhir/R4/subscription.html)

## Supported Interactions

- **read** — Read a single resource by ID (`GET /fhir/R4/{Resource}/{id}`)
- **search-type** — Search resources with query parameters (`GET /fhir/R4/{Resource}?...`)
- **create** — Create a new resource (`POST /fhir/R4/{Resource}`)
- **update** — Update an existing resource (`PUT /fhir/R4/{Resource}/{id}`)
- **delete** — delete
- **$replay** — operation
- **$stats** — operation
- **$deliveries** — operation

## Scopes

Scopes are shared across FHIR releases — the same `system/Subscription.*` scope grants Subscription access on both `/fhir/R4` and `/fhir/R5`.

- <Scope name="system/Subscription.rs" /> — see [scope reference](/v1/scopes#system-subscription-rs)
- <Scope name="system/Subscription.crud" /> — see [scope reference](/v1/scopes#system-subscription-crud)

## Search Parameters

| Parameter | Type | Notes |
|-----------|------|-------|
| `_id` | token | Exact match. For identifiers use `system|value` format. |
| `status` | token | Exact match. For identifiers use `system|value` format. |
| `_count` | number | Integer. For `_count`: default 20, max 100. |
| `_offset` | number | Integer. For `_count`: default 20, max 100. |

## Endpoints

### Read Subscription

Retrieve a single outbound-webhook Subscription by its ID. The response
NEVER includes the signing secret — the secret is returned exactly once,
on create.

<Endpoint method="GET" path="/fhir/R4/Subscription/{id}" />

**Required scope:** <Scope name="system/Subscription.rs" />

#### Code Samples

:::CodeGroup

```bash {label="cURL"}
TOKEN=$(curl -s -X POST https://api.huli.ai/auth/token \
  -d "grant_type=client_credentials" \
  -d "client_assertion_type=urn:ietf:params:oauth:client-assertion-type:jwt-bearer" \
  -d "client_assertion=${CLIENT_ASSERTION}" \
  -d "scope=system/Subscription.rs" \
  | jq -r .access_token)

curl -X GET https://api.huli.ai/fhir/R4/Subscription/${RESOURCE_ID} \
  -H "Authorization: Bearer ${TOKEN}"
```

```typescript {label="TypeScript"}
const token = process.env.HULI_ACCESS_TOKEN ?? "";

const response = await fetch(
  `https://api.huli.ai/fhir/R4/Subscription/${id}`,
  {
    method: "GET",
    headers: {
      "Authorization": `Bearer ${token}`,
    },

  }
);

if (!response.ok) throw new Error(`HTTP ${String(response.status)}`);
const data: unknown = await response.json();
```

```python {label="Python"}
import os
import requests

token = os.environ["HULI_ACCESS_TOKEN"]
headers = {"Authorization": f"Bearer {token}"}

resp = requests.get(
    f"https://api.huli.ai/fhir/R4/Subscription/{resource_id}",
    headers=headers,
)
resp.raise_for_status()
print(resp.json())
```

```java {label="Java"}
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;

public class getSubscriptionExample {
    public static void main(String[] args) throws Exception {
        String token = System.getenv("HULI_ACCESS_TOKEN");
        String resourceId = "RESOURCE_ID";

        HttpClient client = HttpClient.newHttpClient();
        HttpRequest request = HttpRequest.newBuilder()
        .uri(URI.create("https://api.huli.ai/fhir/R4/Subscription/" + resourceId))
        .header("Authorization", "Bearer " + token)
        .header("Accept", "application/fhir+json")
        .method("GET", HttpRequest.BodyPublishers.noBody())
        .build();

        HttpResponse<String> response =
        client.send(request, HttpResponse.BodyHandlers.ofString());
        System.out.println("status: " + response.statusCode());
        System.out.println(response.body());
    }
}
```

```go {label="Go"}
import (
    "fmt"
    "net/http"
    "os"
)

func getSubscriptionExample() {
    token := os.Getenv("HULI_ACCESS_TOKEN")
    req, _ := http.NewRequest("GET", "https://api.huli.ai/fhir/R4/Subscription/"+resourceID+"", nil)
    req.Header.Set("Authorization", "Bearer "+token)

    resp, err := http.DefaultClient.Do(req)
    if err != nil {
        fmt.Println("error:", err)
        return
    }
    defer resp.Body.Close()
    fmt.Println("status:", resp.Status)
}
```

:::

#### Errors

| Code | Status | Description |
|------|--------|-------------|
| [HPB-00106](/v1/errors#hpb-00106) | 401 | Authentication failed |
| [HPB-00104](/v1/errors#hpb-00104) | 403 | Insufficient scope |
| [HPB-00102](/v1/errors#hpb-00102) | 404 | Resource not found |
| [HPB-00105](/v1/errors#hpb-00105) | 429 | Rate limit exceeded |

---

### Update Subscription

Update an existing outbound-webhook Subscription. The mutable fields are
`status`, `criteria`, and the `channel` target/payload/headers. The
signing secret is PRESERVED (never rotated or returned here). The endpoint
is re-validated (https-only, SSRF-safe) on every update. `channel.type`
must remain `rest-hook` and `channel.payload` must remain
`application/fhir+json`.

<Endpoint method="PUT" path="/fhir/R4/Subscription/{id}" />

**Required scope:** <Scope name="system/Subscription.crud" />

#### Request Body

<ParamTable :rows='[{&quot;name&quot;:&quot;resourceType&quot;,&quot;type&quot;:&quot;string&quot;,&quot;required&quot;:true,&quot;description&quot;:&quot;&quot;},{&quot;name&quot;:&quot;id&quot;,&quot;type&quot;:&quot;string(uuid)&quot;,&quot;required&quot;:false,&quot;description&quot;:&quot;&quot;},{&quot;name&quot;:&quot;meta&quot;,&quot;type&quot;:&quot;object&quot;,&quot;required&quot;:false,&quot;description&quot;:&quot;&quot;},{&quot;name&quot;:&quot;status&quot;,&quot;type&quot;:&quot;string&quot;,&quot;required&quot;:true,&quot;description&quot;:&quot;Subscription status. On write accept `requested` (default), `active`,\nor `off`. `error` is server-owned (set by the delivery circuit\nbreaker) and is rejected on write.\n&quot;},{&quot;name&quot;:&quot;reason&quot;,&quot;type&quot;:&quot;string&quot;,&quot;required&quot;:true,&quot;description&quot;:&quot;Required free-text reason for the subscription. Accepted and validated\nnon-empty, but NOT persisted — reads echo a fixed reason.\n&quot;},{&quot;name&quot;:&quot;criteria&quot;,&quot;type&quot;:&quot;string&quot;,&quot;required&quot;:true,&quot;description&quot;:&quot;The FHIR resource type to notify on — one of `Patient`,\n`Appointment`, `Encounter`, `Observation`, `MedicationRequest`,\n`ServiceRequest`. v1 matches on resource type\nonly: query-parameter filtering (e.g. `Encounter?status=finished`) is\nnot yet supported and is rejected, so subscribe to the bare resource\ntype.\n&quot;},{&quot;name&quot;:&quot;channel&quot;,&quot;type&quot;:&quot;object&quot;,&quot;required&quot;:true,&quot;description&quot;:&quot;&quot;},{&quot;name&quot;:&quot;extension&quot;,&quot;type&quot;:&quot;array&quot;,&quot;required&quot;:false,&quot;description&quot;:&quot;On the create (`201`) response ONLY, carries the one-time plaintext\nsigning secret under the\n`https://huli.io/fhir/StructureDefinition/subscription-signing-secret`\nextension (`valueString`). Never present on read / search / update.\n&quot;}]' />

#### Request Example

```json
{
  "resourceType": "Subscription",
  "status": "active",
  "reason": "Notify our EHR mirror of encounter changes",
  "criteria": "Encounter",
  "channel": {
    "type": "rest-hook",
    "endpoint": "https://example.org/webhooks/huli",
    "payload": "application/fhir+json",
    "header": [
      "X-Source-System: acme-ehr"
    ]
  }
}
```

#### Code Samples

:::CodeGroup

```bash {label="cURL"}
TOKEN=$(curl -s -X POST https://api.huli.ai/auth/token \
  -d "grant_type=client_credentials" \
  -d "client_assertion_type=urn:ietf:params:oauth:client-assertion-type:jwt-bearer" \
  -d "client_assertion=${CLIENT_ASSERTION}" \
  -d "scope=system/Subscription.crud" \
  | jq -r .access_token)

curl -X PUT https://api.huli.ai/fhir/R4/Subscription/${RESOURCE_ID} \
  -H "Authorization: Bearer ${TOKEN}" \
  -H "Content-Type: application/fhir+json" \
  -d @resource.json
```

```typescript {label="TypeScript"}
const token = process.env.HULI_ACCESS_TOKEN ?? "";

const response = await fetch(
  `https://api.huli.ai/fhir/R4/Subscription/${id}`,
  {
    method: "PUT",
    headers: {
      "Authorization": `Bearer ${token}`,
      "Content-Type": "application/fhir+json",
    },
    body: JSON.stringify(payload),
  }
);

if (!response.ok) throw new Error(`HTTP ${String(response.status)}`);
const data: unknown = await response.json();
```

```python {label="Python"}
import os
import requests

token = os.environ["HULI_ACCESS_TOKEN"]
headers = {"Authorization": f"Bearer {token}", "Content-Type": "application/fhir+json"}

resp = requests.put(
    f"https://api.huli.ai/fhir/R4/Subscription/{resource_id}",
    headers=headers,
    json=payload,
)
resp.raise_for_status()
print(resp.json())
```

```java {label="Java"}
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;

public class updateSubscriptionExample {
    public static void main(String[] args) throws Exception {
        String token = System.getenv("HULI_ACCESS_TOKEN");
        String resourceId = "RESOURCE_ID";
        String payload = "{}"; // your serialized FHIR resource

        HttpClient client = HttpClient.newHttpClient();
        HttpRequest request = HttpRequest.newBuilder()
        .uri(URI.create("https://api.huli.ai/fhir/R4/Subscription/" + resourceId))
        .header("Authorization", "Bearer " + token)
        .header("Accept", "application/fhir+json")
        .header("Content-Type", "application/fhir+json")
        .method("PUT", HttpRequest.BodyPublishers.ofString(payload))
        .build();

        HttpResponse<String> response =
        client.send(request, HttpResponse.BodyHandlers.ofString());
        System.out.println("status: " + response.statusCode());
        System.out.println(response.body());
    }
}
```

```go {label="Go"}
import (
    "fmt"
    "net/http"
    "os"
)

func updateSubscriptionExample() {
    token := os.Getenv("HULI_ACCESS_TOKEN")
    req, _ := http.NewRequest("PUT", "https://api.huli.ai/fhir/R4/Subscription/"+resourceID+"", body)
    req.Header.Set("Authorization", "Bearer "+token)
    req.Header.Set("Content-Type", "application/fhir+json")
    // set req.Body to your serialized resource
    resp, err := http.DefaultClient.Do(req)
    if err != nil {
        fmt.Println("error:", err)
        return
    }
    defer resp.Body.Close()
    fmt.Println("status:", resp.Status)
}
```

:::

#### Errors

| Code | Status | Description |
|------|--------|-------------|
| [HPB-00101](/v1/errors#hpb-00101) | 400 | Validation error |
| [HPB-00106](/v1/errors#hpb-00106) | 401 | Authentication failed |
| [HPB-00104](/v1/errors#hpb-00104) | 403 | Insufficient scope |
| [HPB-00102](/v1/errors#hpb-00102) | 404 | Resource not found |
| [HPB-00105](/v1/errors#hpb-00105) | 429 | Rate limit exceeded |

---

### Delete Subscription

Soft-delete an outbound-webhook Subscription. The subscription is
deactivated (no further deliveries fan out to it). Returns `204` on
success.

<Endpoint method="DELETE" path="/fhir/R4/Subscription/{id}" />

**Required scope:** <Scope name="system/Subscription.crud" />

#### Code Samples

:::CodeGroup

```bash {label="cURL"}
TOKEN=$(curl -s -X POST https://api.huli.ai/auth/token \
  -d "grant_type=client_credentials" \
  -d "client_assertion_type=urn:ietf:params:oauth:client-assertion-type:jwt-bearer" \
  -d "client_assertion=${CLIENT_ASSERTION}" \
  -d "scope=system/Subscription.crud" \
  | jq -r .access_token)

curl -X DELETE https://api.huli.ai/fhir/R4/Subscription/${RESOURCE_ID} \
  -H "Authorization: Bearer ${TOKEN}"
```

```typescript {label="TypeScript"}
const token = process.env.HULI_ACCESS_TOKEN ?? "";

const response = await fetch(
  `https://api.huli.ai/fhir/R4/Subscription/${id}`,
  {
    method: "DELETE",
    headers: {
      "Authorization": `Bearer ${token}`,
    },

  }
);

if (!response.ok) throw new Error(`HTTP ${String(response.status)}`);
const data: unknown = await response.json();
```

```python {label="Python"}
import os
import requests

token = os.environ["HULI_ACCESS_TOKEN"]
headers = {"Authorization": f"Bearer {token}"}

resp = requests.delete(
    f"https://api.huli.ai/fhir/R4/Subscription/{resource_id}",
    headers=headers,
)
resp.raise_for_status()
print(resp.json())
```

```java {label="Java"}
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;

public class deleteSubscriptionExample {
    public static void main(String[] args) throws Exception {
        String token = System.getenv("HULI_ACCESS_TOKEN");
        String resourceId = "RESOURCE_ID";

        HttpClient client = HttpClient.newHttpClient();
        HttpRequest request = HttpRequest.newBuilder()
        .uri(URI.create("https://api.huli.ai/fhir/R4/Subscription/" + resourceId))
        .header("Authorization", "Bearer " + token)
        .header("Accept", "application/fhir+json")
        .method("DELETE", HttpRequest.BodyPublishers.noBody())
        .build();

        HttpResponse<String> response =
        client.send(request, HttpResponse.BodyHandlers.ofString());
        System.out.println("status: " + response.statusCode());
        System.out.println(response.body());
    }
}
```

```go {label="Go"}
import (
    "fmt"
    "net/http"
    "os"
)

func deleteSubscriptionExample() {
    token := os.Getenv("HULI_ACCESS_TOKEN")
    req, _ := http.NewRequest("DELETE", "https://api.huli.ai/fhir/R4/Subscription/"+resourceID+"", nil)
    req.Header.Set("Authorization", "Bearer "+token)

    resp, err := http.DefaultClient.Do(req)
    if err != nil {
        fmt.Println("error:", err)
        return
    }
    defer resp.Body.Close()
    fmt.Println("status:", resp.Status)
}
```

:::

#### Errors

| Code | Status | Description |
|------|--------|-------------|
| [HPB-00106](/v1/errors#hpb-00106) | 401 | Authentication failed |
| [HPB-00104](/v1/errors#hpb-00104) | 403 | Insufficient scope |
| [HPB-00102](/v1/errors#hpb-00102) | 404 | Resource not found |
| [HPB-00105](/v1/errors#hpb-00105) | 429 | Rate limit exceeded |

---

### Replay Subscription events

Re-scan the organization's event history for this subscription's criteria
(resource type) over a time window and re-enqueue each matching event —
for outage recovery / backfill. NOTE: the scan is by criteria + window, so
it includes events that predate the subscription and events already
delivered; dedupe on `X-Huli-Event-Id`. An event that already has an
in-flight replay for this subscription is skipped, so repeated calls over
the same window don't duplicate. Each newly matched event creates a fresh
delivery carrying the SAME `X-Huli-Event-Id` as the original (so receivers
dedup by event) plus `X-Huli-Replay: true`. The window is clamped to the
30-day retention horizon (`from` floored at `now-30d`, `to` capped at
`now`) and a single call re-enqueues at most 500 events (for a larger
backlog, call again with the same window — in-flight replays are skipped,
so each call advances to older events — until `truncated` is false).
Only an **active** subscription may
replay. The `from`/`to` window is supplied as a FHIR `Parameters` body
(or as `from`/`to` RFC3339 query params); `from` is required, `to`
defaults to now.

<Endpoint method="POST" path="/fhir/R4/Subscription/{id}/$replay" />

**Required scope:** <Scope name="system/Subscription.crud" />

#### Request Example

```json
{
  "resourceType": "Parameters",
  "parameter": [
    {
      "name": "from",
      "valueInstant": "2026-06-01T00:00:00Z"
    },
    {
      "name": "to",
      "valueInstant": "2026-06-02T00:00:00Z"
    }
  ]
}
```

#### Code Samples

:::CodeGroup

```bash {label="cURL"}
TOKEN=$(curl -s -X POST https://api.huli.ai/auth/token \
  -d "grant_type=client_credentials" \
  -d "client_assertion_type=urn:ietf:params:oauth:client-assertion-type:jwt-bearer" \
  -d "client_assertion=${CLIENT_ASSERTION}" \
  -d "scope=system/Subscription.crud" \
  | jq -r .access_token)

curl -X POST https://api.huli.ai/fhir/R4/Subscription/${RESOURCE_ID}/$replay \
  -H "Authorization: Bearer ${TOKEN}" \
  -H "Content-Type: application/fhir+json" \
  -d @$replay.json
```

```typescript {label="TypeScript"}
const token = process.env.HULI_ACCESS_TOKEN ?? "";

const response = await fetch(
  `https://api.huli.ai/fhir/R4/Subscription/${id}/$replay`,
  {
    method: "POST",
    headers: {
      "Authorization": `Bearer ${token}`,
      "Content-Type": "application/fhir+json",
    },
    body: JSON.stringify(payload),
  }
);

if (!response.ok) throw new Error(`HTTP ${String(response.status)}`);
const data: unknown = await response.json();
```

```python {label="Python"}
import os
import requests

token = os.environ["HULI_ACCESS_TOKEN"]
headers = {"Authorization": f"Bearer {token}", "Content-Type": "application/fhir+json"}

resp = requests.post(
    f"https://api.huli.ai/fhir/R4/Subscription/{resource_id}/$replay",
    headers=headers,
    json=payload,
)
resp.raise_for_status()
print(resp.json())
```

```java {label="Java"}
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;

public class replaySubscriptionExample {
    public static void main(String[] args) throws Exception {
        String token = System.getenv("HULI_ACCESS_TOKEN");
        String resourceId = "RESOURCE_ID";
        String payload = "{}"; // your serialized FHIR resource

        HttpClient client = HttpClient.newHttpClient();
        HttpRequest request = HttpRequest.newBuilder()
        .uri(URI.create("https://api.huli.ai/fhir/R4/Subscription/" + resourceId + "/$replay"))
        .header("Authorization", "Bearer " + token)
        .header("Accept", "application/fhir+json")
        .header("Content-Type", "application/fhir+json")
        .method("POST", HttpRequest.BodyPublishers.ofString(payload))
        .build();

        HttpResponse<String> response =
        client.send(request, HttpResponse.BodyHandlers.ofString());
        System.out.println("status: " + response.statusCode());
        System.out.println(response.body());
    }
}
```

```go {label="Go"}
import (
    "fmt"
    "net/http"
    "os"
)

func replaySubscriptionExample() {
    token := os.Getenv("HULI_ACCESS_TOKEN")
    req, _ := http.NewRequest("POST", "https://api.huli.ai/fhir/R4/Subscription/"+resourceID+"/$replay", body)
    req.Header.Set("Authorization", "Bearer "+token)
    req.Header.Set("Content-Type", "application/fhir+json")
    // set req.Body to your serialized resource
    resp, err := http.DefaultClient.Do(req)
    if err != nil {
        fmt.Println("error:", err)
        return
    }
    defer resp.Body.Close()
    fmt.Println("status:", resp.Status)
}
```

:::

#### Errors

| Code | Status | Description |
|------|--------|-------------|
| [HPB-00101](/v1/errors#hpb-00101) | 400 | Validation error |
| [HPB-00106](/v1/errors#hpb-00106) | 401 | Authentication failed |
| [HPB-00104](/v1/errors#hpb-00104) | 403 | Insufficient scope |
| [HPB-00102](/v1/errors#hpb-00102) | 404 | Resource not found |
| [HPB-00101](/v1/errors#hpb-00101) | 422 | Unprocessable entity |
| [HPB-00105](/v1/errors#hpb-00105) | 429 | Rate limit exceeded |

---

### Subscription delivery metrics

Per-subscription delivery observability. Returns a FHIR `Parameters`
resource with the delivered / failed / pending / dead counts, the
dead-letter depth, total attempts, the terminal success rate, and the
enqueue-to-delivery latency p50/p95 (seconds). Never exposes payloads,
endpoints, or signing secrets.

<Endpoint method="GET" path="/fhir/R4/Subscription/{id}/$stats" />

**Required scope:** <Scope name="system/Subscription.rs" />

#### Code Samples

:::CodeGroup

```bash {label="cURL"}
TOKEN=$(curl -s -X POST https://api.huli.ai/auth/token \
  -d "grant_type=client_credentials" \
  -d "client_assertion_type=urn:ietf:params:oauth:client-assertion-type:jwt-bearer" \
  -d "client_assertion=${CLIENT_ASSERTION}" \
  -d "scope=system/Subscription.rs" \
  | jq -r .access_token)

curl -X GET https://api.huli.ai/fhir/R4/Subscription/${RESOURCE_ID}/$stats \
  -H "Authorization: Bearer ${TOKEN}"
```

```typescript {label="TypeScript"}
const token = process.env.HULI_ACCESS_TOKEN ?? "";

const response = await fetch(
  `https://api.huli.ai/fhir/R4/Subscription/${id}/$stats`,
  {
    method: "GET",
    headers: {
      "Authorization": `Bearer ${token}`,
    },

  }
);

if (!response.ok) throw new Error(`HTTP ${String(response.status)}`);
const data: unknown = await response.json();
```

```python {label="Python"}
import os
import requests

token = os.environ["HULI_ACCESS_TOKEN"]
headers = {"Authorization": f"Bearer {token}"}

resp = requests.get(
    f"https://api.huli.ai/fhir/R4/Subscription/{resource_id}/$stats",
    headers=headers,
)
resp.raise_for_status()
print(resp.json())
```

```java {label="Java"}
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;

public class getSubscriptionStatsExample {
    public static void main(String[] args) throws Exception {
        String token = System.getenv("HULI_ACCESS_TOKEN");
        String resourceId = "RESOURCE_ID";

        HttpClient client = HttpClient.newHttpClient();
        HttpRequest request = HttpRequest.newBuilder()
        .uri(URI.create("https://api.huli.ai/fhir/R4/Subscription/" + resourceId + "/$stats"))
        .header("Authorization", "Bearer " + token)
        .header("Accept", "application/fhir+json")
        .method("GET", HttpRequest.BodyPublishers.noBody())
        .build();

        HttpResponse<String> response =
        client.send(request, HttpResponse.BodyHandlers.ofString());
        System.out.println("status: " + response.statusCode());
        System.out.println(response.body());
    }
}
```

```go {label="Go"}
import (
    "fmt"
    "net/http"
    "os"
)

func getSubscriptionStatsExample() {
    token := os.Getenv("HULI_ACCESS_TOKEN")
    req, _ := http.NewRequest("GET", "https://api.huli.ai/fhir/R4/Subscription/"+resourceID+"/$stats", nil)
    req.Header.Set("Authorization", "Bearer "+token)

    resp, err := http.DefaultClient.Do(req)
    if err != nil {
        fmt.Println("error:", err)
        return
    }
    defer resp.Body.Close()
    fmt.Println("status:", resp.Status)
}
```

:::

#### Errors

| Code | Status | Description |
|------|--------|-------------|
| [HPB-00106](/v1/errors#hpb-00106) | 401 | Authentication failed |
| [HPB-00104](/v1/errors#hpb-00104) | 403 | Insufficient scope |
| [HPB-00102](/v1/errors#hpb-00102) | 404 | Resource not found |
| [HPB-00105](/v1/errors#hpb-00105) | 429 | Rate limit exceeded |

---

### Subscription delivery-attempt trail

The delivery-level audit trail for one subscription: the recent delivery
attempts (newest first, paged via `_count`, optionally filtered by
`status` = pending | delivered | failed | dead), returned as a FHIR
`Parameters` resource with one `delivery` group per attempt. Each group
carries only sanitized metadata — delivery status, attempt count, last
HTTP status code, queued/delivered timestamps, the replay flag, and the
source event's type — never the payload, endpoint, signing secret, or
error text.

<Endpoint method="GET" path="/fhir/R4/Subscription/{id}/$deliveries" />

**Required scope:** <Scope name="system/Subscription.rs" />

#### Query Parameters

<ParamTable :rows='[{&quot;name&quot;:&quot;_count&quot;,&quot;type&quot;:&quot;integer&quot;,&quot;required&quot;:false,&quot;description&quot;:&quot;Maximum number of delivery attempts to return.&quot;},{&quot;name&quot;:&quot;status&quot;,&quot;type&quot;:&quot;string&quot;,&quot;required&quot;:false,&quot;description&quot;:&quot;Narrow the trail to a single delivery status.&quot;}]' />

#### Code Samples

:::CodeGroup

```bash {label="cURL"}
TOKEN=$(curl -s -X POST https://api.huli.ai/auth/token \
  -d "grant_type=client_credentials" \
  -d "client_assertion_type=urn:ietf:params:oauth:client-assertion-type:jwt-bearer" \
  -d "client_assertion=${CLIENT_ASSERTION}" \
  -d "scope=system/Subscription.rs" \
  | jq -r .access_token)

curl -X GET https://api.huli.ai/fhir/R4/Subscription/${RESOURCE_ID}/$deliveries \
  -H "Authorization: Bearer ${TOKEN}"
```

```typescript {label="TypeScript"}
const token = process.env.HULI_ACCESS_TOKEN ?? "";

const response = await fetch(
  `https://api.huli.ai/fhir/R4/Subscription/${id}/$deliveries`,
  {
    method: "GET",
    headers: {
      "Authorization": `Bearer ${token}`,
    },

  }
);

if (!response.ok) throw new Error(`HTTP ${String(response.status)}`);
const data: unknown = await response.json();
```

```python {label="Python"}
import os
import requests

token = os.environ["HULI_ACCESS_TOKEN"]
headers = {"Authorization": f"Bearer {token}"}

resp = requests.get(
    f"https://api.huli.ai/fhir/R4/Subscription/{resource_id}/$deliveries",
    headers=headers,
)
resp.raise_for_status()
print(resp.json())
```

```java {label="Java"}
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;

public class getSubscriptionDeliveriesExample {
    public static void main(String[] args) throws Exception {
        String token = System.getenv("HULI_ACCESS_TOKEN");
        String resourceId = "RESOURCE_ID";

        HttpClient client = HttpClient.newHttpClient();
        HttpRequest request = HttpRequest.newBuilder()
        .uri(URI.create("https://api.huli.ai/fhir/R4/Subscription/" + resourceId + "/$deliveries"))
        .header("Authorization", "Bearer " + token)
        .header("Accept", "application/fhir+json")
        .method("GET", HttpRequest.BodyPublishers.noBody())
        .build();

        HttpResponse<String> response =
        client.send(request, HttpResponse.BodyHandlers.ofString());
        System.out.println("status: " + response.statusCode());
        System.out.println(response.body());
    }
}
```

```go {label="Go"}
import (
    "fmt"
    "net/http"
    "os"
)

func getSubscriptionDeliveriesExample() {
    token := os.Getenv("HULI_ACCESS_TOKEN")
    req, _ := http.NewRequest("GET", "https://api.huli.ai/fhir/R4/Subscription/"+resourceID+"/$deliveries", nil)
    req.Header.Set("Authorization", "Bearer "+token)

    resp, err := http.DefaultClient.Do(req)
    if err != nil {
        fmt.Println("error:", err)
        return
    }
    defer resp.Body.Close()
    fmt.Println("status:", resp.Status)
}
```

:::

#### Errors

| Code | Status | Description |
|------|--------|-------------|
| [HPB-00101](/v1/errors#hpb-00101) | 400 | Validation error |
| [HPB-00106](/v1/errors#hpb-00106) | 401 | Authentication failed |
| [HPB-00104](/v1/errors#hpb-00104) | 403 | Insufficient scope |
| [HPB-00102](/v1/errors#hpb-00102) | 404 | Resource not found |
| [HPB-00105](/v1/errors#hpb-00105) | 429 | Rate limit exceeded |

---

### Search Subscriptions

Search outbound-webhook Subscriptions for the authenticated organization.
Supports `_id` and `status` filters plus offset pagination. Responses
NEVER include the signing secret.

<Endpoint method="GET" path="/fhir/R4/Subscription" />

**Required scope:** <Scope name="system/Subscription.rs" />

#### Query Parameters

<ParamTable :rows='[{&quot;name&quot;:&quot;_id&quot;,&quot;type&quot;:&quot;string&quot;,&quot;required&quot;:false,&quot;description&quot;:&quot;Filter by Subscription ID (UUID).&quot;},{&quot;name&quot;:&quot;status&quot;,&quot;type&quot;:&quot;string&quot;,&quot;required&quot;:false,&quot;description&quot;:&quot;Filter by status: `requested`, `active`, `error`, `off`.&quot;},{&quot;name&quot;:&quot;_count&quot;,&quot;type&quot;:&quot;integer&quot;,&quot;required&quot;:false,&quot;description&quot;:&quot;Number of results per page (default: 20, max: 100).&quot;},{&quot;name&quot;:&quot;_offset&quot;,&quot;type&quot;:&quot;integer&quot;,&quot;required&quot;:false,&quot;description&quot;:&quot;Number of results to skip (offset-based pagination).&quot;}]' />

#### Code Samples

:::CodeGroup

```bash {label="cURL"}
TOKEN=$(curl -s -X POST https://api.huli.ai/auth/token \
  -d "grant_type=client_credentials" \
  -d "client_assertion_type=urn:ietf:params:oauth:client-assertion-type:jwt-bearer" \
  -d "client_assertion=${CLIENT_ASSERTION}" \
  -d "scope=system/Subscription.rs" \
  | jq -r .access_token)

curl -X GET https://api.huli.ai/fhir/R4/Subscription \
  -H "Authorization: Bearer ${TOKEN}"
```

```typescript {label="TypeScript"}
const token = process.env.HULI_ACCESS_TOKEN ?? "";

const response = await fetch(
  `https://api.huli.ai/fhir/R4/Subscription`,
  {
    method: "GET",
    headers: {
      "Authorization": `Bearer ${token}`,
    },

  }
);

if (!response.ok) throw new Error(`HTTP ${String(response.status)}`);
const data: unknown = await response.json();
```

```python {label="Python"}
import os
import requests

token = os.environ["HULI_ACCESS_TOKEN"]
headers = {"Authorization": f"Bearer {token}"}

resp = requests.get(
    f"https://api.huli.ai/fhir/R4/Subscription",
    headers=headers,
)
resp.raise_for_status()
print(resp.json())
```

```java {label="Java"}
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;

public class searchSubscriptionsExample {
    public static void main(String[] args) throws Exception {
        String token = System.getenv("HULI_ACCESS_TOKEN");

        HttpClient client = HttpClient.newHttpClient();
        HttpRequest request = HttpRequest.newBuilder()
        .uri(URI.create("https://api.huli.ai/fhir/R4/Subscription"))
        .header("Authorization", "Bearer " + token)
        .header("Accept", "application/fhir+json")
        .method("GET", HttpRequest.BodyPublishers.noBody())
        .build();

        HttpResponse<String> response =
        client.send(request, HttpResponse.BodyHandlers.ofString());
        System.out.println("status: " + response.statusCode());
        System.out.println(response.body());
    }
}
```

```go {label="Go"}
import (
    "fmt"
    "net/http"
    "os"
)

func searchSubscriptionsExample() {
    token := os.Getenv("HULI_ACCESS_TOKEN")
    req, _ := http.NewRequest("GET", "https://api.huli.ai/fhir/R4/Subscription", nil)
    req.Header.Set("Authorization", "Bearer "+token)

    resp, err := http.DefaultClient.Do(req)
    if err != nil {
        fmt.Println("error:", err)
        return
    }
    defer resp.Body.Close()
    fmt.Println("status:", resp.Status)
}
```

:::

#### Errors

| Code | Status | Description |
|------|--------|-------------|
| [HPB-00106](/v1/errors#hpb-00106) | 401 | Authentication failed |
| [HPB-00104](/v1/errors#hpb-00104) | 403 | Insufficient scope |
| [HPB-00105](/v1/errors#hpb-00105) | 429 | Rate limit exceeded |

---

### Create Subscription

Create an outbound-webhook Subscription. The server assigns the resource
ID and generates an HMAC signing secret, which is returned EXACTLY ONCE in
the `201` response body as a `valueString` extension
(`https://huli.io/fhir/StructureDefinition/subscription-signing-secret`)
with a `Cache-Control: no-store` response header. The secret is never
returned again — store it securely.
Validation (all fail-closed, `400`):
- `channel.type` must be `rest-hook`.
- `channel.endpoint` must be an `https` URL with no embedded credentials
  (SSRF-checked).
- `channel.payload`, when set, must be `application/fhir+json`.
- `channel.header` entries must be `Name: value` (no CR/LF, no duplicates).
- `criteria` must be a bare resource type — one of `Patient`,
  `Appointment`, `Encounter`, `Observation`, `MedicationRequest`,
  `ServiceRequest`. Query-parameter filtering is
  not supported in v1 (fan-out matches on resource type only); a
  `criteria` carrying a query (e.g. `Encounter?status=finished`) is
  rejected.
- `reason` is required.
The `201` body also carries a non-blocking warning `OperationOutcome`
(in `contained`) reminding the integrator of their BAA / PHI-handling
responsibility for the configured endpoint.

<Endpoint method="POST" path="/fhir/R4/Subscription" />

**Required scope:** <Scope name="system/Subscription.crud" />

#### Request Body

<ParamTable :rows='[{&quot;name&quot;:&quot;resourceType&quot;,&quot;type&quot;:&quot;string&quot;,&quot;required&quot;:true,&quot;description&quot;:&quot;&quot;},{&quot;name&quot;:&quot;id&quot;,&quot;type&quot;:&quot;string(uuid)&quot;,&quot;required&quot;:false,&quot;description&quot;:&quot;&quot;},{&quot;name&quot;:&quot;meta&quot;,&quot;type&quot;:&quot;object&quot;,&quot;required&quot;:false,&quot;description&quot;:&quot;&quot;},{&quot;name&quot;:&quot;status&quot;,&quot;type&quot;:&quot;string&quot;,&quot;required&quot;:true,&quot;description&quot;:&quot;Subscription status. On write accept `requested` (default), `active`,\nor `off`. `error` is server-owned (set by the delivery circuit\nbreaker) and is rejected on write.\n&quot;},{&quot;name&quot;:&quot;reason&quot;,&quot;type&quot;:&quot;string&quot;,&quot;required&quot;:true,&quot;description&quot;:&quot;Required free-text reason for the subscription. Accepted and validated\nnon-empty, but NOT persisted — reads echo a fixed reason.\n&quot;},{&quot;name&quot;:&quot;criteria&quot;,&quot;type&quot;:&quot;string&quot;,&quot;required&quot;:true,&quot;description&quot;:&quot;The FHIR resource type to notify on — one of `Patient`,\n`Appointment`, `Encounter`, `Observation`, `MedicationRequest`,\n`ServiceRequest`. v1 matches on resource type\nonly: query-parameter filtering (e.g. `Encounter?status=finished`) is\nnot yet supported and is rejected, so subscribe to the bare resource\ntype.\n&quot;},{&quot;name&quot;:&quot;channel&quot;,&quot;type&quot;:&quot;object&quot;,&quot;required&quot;:true,&quot;description&quot;:&quot;&quot;},{&quot;name&quot;:&quot;extension&quot;,&quot;type&quot;:&quot;array&quot;,&quot;required&quot;:false,&quot;description&quot;:&quot;On the create (`201`) response ONLY, carries the one-time plaintext\nsigning secret under the\n`https://huli.io/fhir/StructureDefinition/subscription-signing-secret`\nextension (`valueString`). Never present on read / search / update.\n&quot;}]' />

#### Request Example

```json
{
  "resourceType": "Subscription",
  "status": "requested",
  "reason": "Notify our EHR mirror of encounter changes",
  "criteria": "Encounter",
  "channel": {
    "type": "rest-hook",
    "endpoint": "https://example.org/webhooks/huli",
    "payload": "application/fhir+json",
    "header": [
      "X-Source-System: acme-ehr"
    ]
  }
}
```

#### Code Samples

:::CodeGroup

```bash {label="cURL"}
TOKEN=$(curl -s -X POST https://api.huli.ai/auth/token \
  -d "grant_type=client_credentials" \
  -d "client_assertion_type=urn:ietf:params:oauth:client-assertion-type:jwt-bearer" \
  -d "client_assertion=${CLIENT_ASSERTION}" \
  -d "scope=system/Subscription.crud" \
  | jq -r .access_token)

curl -X POST https://api.huli.ai/fhir/R4/Subscription \
  -H "Authorization: Bearer ${TOKEN}" \
  -H "Content-Type: application/fhir+json" \
  -d @subscription.json
```

```typescript {label="TypeScript"}
const token = process.env.HULI_ACCESS_TOKEN ?? "";

const response = await fetch(
  `https://api.huli.ai/fhir/R4/Subscription`,
  {
    method: "POST",
    headers: {
      "Authorization": `Bearer ${token}`,
      "Content-Type": "application/fhir+json",
    },
    body: JSON.stringify(payload),
  }
);

if (!response.ok) throw new Error(`HTTP ${String(response.status)}`);
const data: unknown = await response.json();
```

```python {label="Python"}
import os
import requests

token = os.environ["HULI_ACCESS_TOKEN"]
headers = {"Authorization": f"Bearer {token}", "Content-Type": "application/fhir+json"}

resp = requests.post(
    f"https://api.huli.ai/fhir/R4/Subscription",
    headers=headers,
    json=payload,
)
resp.raise_for_status()
print(resp.json())
```

```java {label="Java"}
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;

public class createSubscriptionExample {
    public static void main(String[] args) throws Exception {
        String token = System.getenv("HULI_ACCESS_TOKEN");
        String payload = "{}"; // your serialized FHIR resource

        HttpClient client = HttpClient.newHttpClient();
        HttpRequest request = HttpRequest.newBuilder()
        .uri(URI.create("https://api.huli.ai/fhir/R4/Subscription"))
        .header("Authorization", "Bearer " + token)
        .header("Accept", "application/fhir+json")
        .header("Content-Type", "application/fhir+json")
        .method("POST", HttpRequest.BodyPublishers.ofString(payload))
        .build();

        HttpResponse<String> response =
        client.send(request, HttpResponse.BodyHandlers.ofString());
        System.out.println("status: " + response.statusCode());
        System.out.println(response.body());
    }
}
```

```go {label="Go"}
import (
    "fmt"
    "net/http"
    "os"
)

func createSubscriptionExample() {
    token := os.Getenv("HULI_ACCESS_TOKEN")
    req, _ := http.NewRequest("POST", "https://api.huli.ai/fhir/R4/Subscription", body)
    req.Header.Set("Authorization", "Bearer "+token)
    req.Header.Set("Content-Type", "application/fhir+json")
    // set req.Body to your serialized resource
    resp, err := http.DefaultClient.Do(req)
    if err != nil {
        fmt.Println("error:", err)
        return
    }
    defer resp.Body.Close()
    fmt.Println("status:", resp.Status)
}
```

:::

#### Errors

| Code | Status | Description |
|------|--------|-------------|
| [HPB-00101](/v1/errors#hpb-00101) | 400 | Validation error |
| [HPB-00106](/v1/errors#hpb-00106) | 401 | Authentication failed |
| [HPB-00104](/v1/errors#hpb-00104) | 403 | Insufficient scope |
| [HPB-00105](/v1/errors#hpb-00105) | 429 | Rate limit exceeded |

---
