---
description: Custom SDC operations supported by Aidbox Forms.
---

> For the complete documentation index, see [llms.txt](https://www.health-samurai.io/docs/formbox/llms.txt).
> Use it to discover all available pages before guessing URLs.

---

# Custom SDC API

* [$generate-link](aidbox-sdc-api.md#generate-a-link-to-a-questionnaireresponse-generate-link)
* [$save](aidbox-sdc-api.md#save-a-questionnaireresponse-save)
* [$submit](aidbox-sdc-api.md#submit-a-questionnaireresponse-submit)
* [$notify-patient](aidbox-sdc-api.md#notify-a-patient-notify-patient)
* [$render](aidbox-sdc-api.md#render-a-questionnaire-or-response-render)

## Generate a link to a QuestionnaireResponse - $generate-link

This operation generates a link to a web page to be used to continue answering a specified [QuestionnaireResponse](https://hl7.org/fhir/R4/questionnaireresponse.html).

### URLs

```
POST [base]/QuestionnaireResponse/[id]/$generate-link
```

### Parameters

{% hint style="warning" %}
NOTE: All parameters wrapped with `Parameters object`

```yaml
resourceType: Parameters
parameter:
- name:  [var-name]
  value: [var-value]
```
{% endhint %}

| Parameter                                                  | Cardinality | Type                                                                                                                                         |
| ---------------------------------------------------------- | ----------- | -------------------------------------------------------------------------------------------------------------------------------------------- |
| [allow-amend](aidbox-sdc-api.md#allow-amend)               | 0..1        | [Boolean](http://hl7.org/fhir/R4/datatypes.html#boolean)                                                                                     |
| [allow-repopulate](aidbox-sdc-api.md#allow-repopulate)     | 0..1        | [Boolean](http://hl7.org/fhir/R4/datatypes.html#boolean)                                                                                     |
| [redirect-on-submit](aidbox-sdc-api.md#redirect-on-submit) | 0..1        | [String](http://hl7.org/fhir/R4/datatypes.html#string)                                                                                       |
| [redirect-on-save](aidbox-sdc-api.md#redirect-on-save)     | 0..1        | [String](http://hl7.org/fhir/R4/datatypes.html#string)                                                                                       |
| [redirect-on-timeout](aidbox-sdc-api.md#redirect-on-timeout)  | 0..1        | [String](http://hl7.org/fhir/R4/datatypes.html#string)                                                                                    |
| [session-timeout](aidbox-sdc-api.md#session-timeout)      | 0..1        | [String](http://hl7.org/fhir/R4/datatypes.html#integer)                                                                                       |
| [warning-timeout](aidbox-sdc-api.md#warning-timeout)      | 0..1        | [String](http://hl7.org/fhir/R4/datatypes.html#integer)                                                                                       |
| [expiration](aidbox-sdc-api.md#expiration)                 | 0..1        | [Integer](http://hl7.org/fhir/R4/datatypes.html#integer)                                                                                     |
| [theme](aidbox-sdc-api.md#theme)                           | 0..1        | [String](http://hl7.org/fhir/R4/datatypes.html#string)                                                                                       |
| [read-only](aidbox-sdc-api.md#read-only)                   | 0..1        | [Boolean](http://hl7.org/fhir/R4/datatypes.html#boolean)                                                                                     |
| [app-name](aidbox-sdc-api.md#read-only)                    | 0..1        | [String](http://hl7.org/fhir/R4/datatypes.html#string)                                                                                       |
| [config](aidbox-sdc-api.md#config)                         | 0..1        | [String](http://hl7.org/fhir/R4/datatypes.html#string)                                                                                       |
| source                                                     |             | [Reference\<Device, Organization, Patient, Practitioner, PractitionerRole, RelatedPerson>](http://hl7.org/fhir/R4/references.html#Reference) |
| partOf                                                     |             | [Reference\<Observation, Procedure>](http://hl7.org/fhir/R4/references.html#Reference)                                                       |
| author                                                     |             | [Reference\<Device, Practitioner, PractitionerRole, Patient, RelatedPerson, Organization>](http://hl7.org/fhir/R4/references.html#Reference) |
| basedOn                                                    |             | [Reference\<CarePlan, ServiceRequest>](http://hl7.org/fhir/R4/references.html#Reference)                                                     |

#### allow-amend

Whether the generated link will allow amending and re-submitting the form.

```
name: allow-amend
value:
  Boolean: true
```

#### allow-repopulate

Whether the generated link will allow re-populating the form.

NOTE: Repopulate will be working only with forms that contain populate behavior

```
name: allow-repopulate
value:
  Boolean: true
```

#### redirect-on-submit

A URL where the user will be redirected to after successfully submitting the form.

```yaml
name: redirect-on-submit
value:
  String: https://example.com/submit-hook?questionnaire=123
```

#### redirect-on-save

A URL where the user will be redirected to after hitting Save button.

> By default `Save button is not visible` - form autosaved after every keystroke. But sometimes it's usefull to close form in a partially-filled state

```yaml
name: redirect-on-save
value:
  String: https://example.com/submit-hook?questionnaire=123
```

#### redirect-on-timeout

URL to open after the session expires.

```yaml
name: redirect-on-timeout
value:
  String: https://example.com/session-expired
```

#### session-timeout

Inactivity timeout duration before the session expires (in minutes).

> By default, shared forms do not expire due to inactivity, and users can complete them without a time limit.

```yaml
name: session-timeout
value:
  Integer: 30
```

#### warning-timeout

Time before expiration when the inactivity warning is shown (in minutes).
Defaults to 1 minute.

```yaml
name: warning-timeout
value:
  Integer: 5
```

#### expiration

Link expiration period (days)

```yaml
name: expiration
value:
  Integer: 30
```

> By default thir parameter = 7 days

#### theme

Form theme.

```yaml
name: theme
value:
  String: hs-theme
```

#### read-only

Show form in a **read-only** mode

```yaml
name: read-only
value:
  Boolean: true
```

**app-name**

Application name that will be used in Audit logging when returned link was used.

> Audit logging should be enabled.

```yaml
- name: app-name
  value
    String: my-app
```

#### config

Reference to a saved `SDCConfig` resource, passed as the resource **id**. When the form is opened via the generated link, the renderer loads this `SDCConfig` and applies its `language`, `translations`, `theme`, and other rendering settings. This is the recommended way to open a shared form in a specific language.

> The value must be the **id** of an existing `SDCConfig` resource — not an inline configuration object. If omitted, the default `SDCConfig` is used.

```yaml
name: config
value:
  String: cd0f1a23-2be3-45f7-b73e-38f4615e2628
```

### Usage Example

{% tabs %}
{% tab title="Request" %}
```http
POST [base]/QuestionnaireResponse/[id]/$generate-link
content-type: text/yaml

resourceType: Parameters
parameter:
  - name: allow-amend
    value:
      Boolean: true
  - name: redirect-on-submit
    value:
      String: https://example.com/submit-hook?questionnaire=123
```
{% endtab %}

{% tab title="Success Response" %}
HTTP status: 200

```yaml
link: http://forms.aidbox.io/ui/sdc#/questionnaire-response/12c1178c-70a9-4e02-a53d-65b13373926e?token=eyJhbGciOiJIUzI
```
{% endtab %}

{% tab title="Failure Response" %}
HTTP status: 422

```yaml
resourceType: OperationOutcome
text:
  status: generated
  div: Parameters are invalid
issue:
- severity: error
  code: invalid
  expression:
  - parameter.0.resource
  diagnostics: unknown key :resource

```
{% endtab %}
{% endtabs %}

> Aidbox uses HS256 to sign JWT token by default. To use RS256 you need to set
>
> `BOX_SECURITY_AUTH_KEYS_PRIVATE` and `BOX_SECURITY_AUTH_KEYS_PUBLIC` environment variables.
>
> [See settings](https://www.health-samurai.io/docs/aidbox/reference/settings/security-and-access-control#security.auth.keys.public)

## Save a QuestionnaireResponse - $save

This operation validates the structure of a QuestionnaireResponse and saves it. It performs basic structural validation, but does not validate against the associated Questionnaire definition.

The operation validates only FHIR structure of QuestionnaireResponse and have associated Questionnaire. Operation doesn't validate for example — required fields like $submit operation

### URLs

```
POST [base]/fhir/QuestionnaireResponse/$save
```

### Parameters

{% hint style="warning" %}
NOTE: All parameters wrapped with `Parameters object`

```yaml
resourceType: Parameters
parameter:
- name: response
  resource:
    # QuestionnaireResponse resource here
```
{% endhint %}

The operation takes a single input parameter named "response" containing a QuestionnaireResponse resource wrapped in a Parameters resource.

### Output Parameters

The operation returns:

* **response**: The saved QuestionnaireResponse resource
* **issues**: Any validation issues encountered (if applicable)

### Usage Example

{% tabs %}
{% tab title="Request" %}
```http
POST [base]/fhir/QuestionnaireResponse/$save
content-type: text/yaml

resourceType: Parameters
parameter:
- name: response
  resource:
    resourceType: QuestionnaireResponse
    questionnaire: Questionnaire/patient-registration
    status: in-progress
    item:
    - linkId: name
      text: Patient Name
      item:
      - linkId: name.given
        text: Given Name
        answer:
        - valueString: John
      - linkId: name.family
        text: Family Name
        answer:
        - valueString: Smith
```
{% endtab %}

{% tab title="Success Response" %}
HTTP status: 200

```yml
resourceType: Parameters
parameter:
- name: response
  resource:
    resourceType: QuestionnaireResponse
    id: 12c1178c-70a9-4e02-a53d-65b13373926e
    questionnaire: Questionnaire/patient-registration
    status: in-progress
    item:
    - linkId: name
      text: Patient Name
      item:
      - linkId: name.given
        text: Given Name
        answer:
        - valueString: John
      - linkId: name.family
        text: Family Name
        answer:
        - valueString: Smith
```
{% endtab %}

{% tab title="Validation Failure Response" %}
HTTP status: 422

```yml
resourceType: Parameters
parameter:
- name: issue
  resource:
    resourceType: OperationOutcome
    issue:
    - severity: fatal
      code: invalid
      expression:
      - QuestionnaireResponse.item[0].item[1].answer[0].valueDecimal
      details:
        coding:
        - system: http://aidbox.app/CodeSystem/operation-outcome-type
          code: invalid-type
        - system: http://aidbox.app/CodeSystem/schema-id
          code: QuestionnaireResponse
      diagnostics: Invalid type for the field. Expected 'string', but got 'decimal'

```
{% endtab %}
{% endtabs %}

## Submit a QuestionnaireResponse - $submit

This operation validates and submits a QuestionnaireResponse, marking it as "completed" or "amended". It performs comprehensive validation against the associated Questionnaire definition. If validation fails, it returns only the "issues" parameter without the "response" parameter and does not save the QuestionnaireResponse.

### URLs

```
POST [base]/fhir/QuestionnaireResponse/$submit
```

### Parameters

{% hint style="warning" %}
NOTE: All parameters wrapped with `Parameters object`

```yml
resourceType: Parameters
parameter:
- name: response
  resource:
    # QuestionnaireResponse resource here
```
{% endhint %}

The operation takes a single input parameter named "response" containing a QuestionnaireResponse resource wrapped in a Parameters resource.

### Output Parameters

The operation returns:

* **response**: The submitted QuestionnaireResponse resource with status updated to "completed"
* **issues**: Any validation issues encountered (if applicable)

### Usage Example

{% tabs %}
{% tab title="Request" %}
```http
POST [base]/fhir/QuestionnaireResponse/$submit
content-type: text/yaml

resourceType: Parameters
parameter:
- name: response
  resource:
    resourceType: QuestionnaireResponse
    questionnaire: Questionnaire/patient-registration
    status: in-progress
    item:
    - linkId: name
      text: Patient Name
      item:
      - linkId: name.given
        text: Given Name
        answer:
        - valueString: John
      - linkId: name.family
        text: Family Name
        answer:
        - valueString: Smith
    - linkId: birthDate
      text: Date of Birth
      answer:
      - valueDate: '1970-01-01'
    - linkId: gender
      text: Gender
      answer:
      - valueCoding:
          system: http://hl7.org/fhir/administrative-gender
          code: male
          display: Male
```
{% endtab %}

{% tab title="Success Response" %}
HTTP status: 200

```yml
resourceType: Parameters
parameter:
- name: response
  resource:
    resourceType: QuestionnaireResponse
    id: 12c1178c-70a9-4e02-a53d-65b13373926e
    questionnaire: Questionnaire/patient-registration
    status: completed
    item:
    - linkId: name
      text: Patient Name
      item:
      - linkId: name.given
        text: Given Name
        answer:
        - valueString: John
      - linkId: name.family
        text: Family Name
        answer:
        - valueString: Smith
    - linkId: birthDate
      text: Date of Birth
      answer:
      - valueDate: '1970-01-01'
    - linkId: gender
      text: Gender
      answer:
      - valueCoding:
          system: http://hl7.org/fhir/administrative-gender
          code: male
          display: Male
```
{% endtab %}

{% tab title="Validation Failure Response" %}
HTTP status: 422

```yml
resourceType: Parameters
parameter:
- name: issues
  resource:
    resourceType: OperationOutcome
    issue:
    - severity: error
      code: required
      expression:
      - QuestionnaireResponse.item[3]
      diagnostics: 'Missing required field: Contact Information'
```
{% endtab %}
{% endtabs %}


## Notify a Patient - $notify-patient

This operation sends an email notification to a patient (or to a provided email) with a generated form link for a QuestionnaireResponse. It sends a single email per request.

If `email` is not provided in a context, the operation tries to resolve the patient email from `QuestionnaireResponse.subject` -> `Patient.telecom.where(system = 'email')`.


### URLs

```
POST [base]/fhir/QuestionnaireResponse/$notify-patient
```

### Parameters

{% hint style="warning" %}
NOTE: All parameters wrapped with `Parameters` object

```yaml
resourceType: Parameters
parameter:
- name: provider
  valueString: smtp-provider # required. One of: smtp-provider, postmark-provider, mailgun-provider, sendgrid-provider
- name: response
  valueReference:
    reference: QuestionnaireResponse/qr1 # required
- name: email
  valueString: joe@mail.com # optional
- name: template
  valueReference:
    reference: NotificationTemplate/my-template # optional
- name: context
  part:
  - name: any-other-field
    valueString: foo # optional, passed to email template payload
```
{% endhint %}

The operation takes:

* **provider** (required): Email provider name. Must be one of `smtp-provider`, `postmark-provider`, `mailgun-provider`, `sendgrid-provider`.
* **response** (required): QuestionnaireResponse reference.
* **email** (optional): Direct recipient email. If missing, patient email from `QuestionnaireResponse.subject` is used.
* **template** (optional): NotificationTemplate reference. If not provided, the default template `sdc-form-link-email` is used.
* **context** (optional): Custom payload fields for template rendering. All fields are passed to the email template payload along with the generated `link`


System takes by default preexisted `NotificationTemplate`, that looks like this.

```
resourceType: NotificationTemplate
id: sdc-form-link-email
subject: New form available
template: 'A new form is ready for you. Open: {{link}}'
```

It's possible to create other templates [see docs](https://www.health-samurai.io/docs/aidbox/modules/integration-toolkit/email-providers)

### Output Parameters

The operation returns `Parameters` that includes:

* **response**: QuestionnaireResponse reference
* **email**: Resolved recipient email
* **status**: `"sent"` or `"failed"`
* **message**: Error message if status is `"failed"`

### Usage Example

{% tabs %}
{% tab title="Request" %}
```yaml
POST [base]/fhir/QuestionnaireResponse/$notify-patient
content-type: text/yaml

resourceType: Parameters
parameter:
- name: provider
  valueString: smtp-provider
- name: response
  valueReference:
    reference: QuestionnaireResponse/qr-direct
- name: email
  valueString: direct@mail.com
- name: context
  part:
  - name: foo
    valueString: bar
```
{% endtab %}

{% tab title="Success Response" %}
HTTP status: 200

```yaml
resourceType: Parameters
parameter:
- name: response
  valueReference:
    reference: QuestionnaireResponse/qr-direct
- name: email
  valueString: direct@mail.com
- name: status
  valueString: sent
```
{% endtab %}

{% tab title="Failure Response" %}
HTTP status: 422

```yaml
resourceType: OperationOutcome
text:
  status: generated
  div: <div xmlns="http://www.w3.org/1999/xhtml"><p>'provider' parameter id is not provided. Should be one of: smtp-provider, postmark-provider, mailgun-provider, sendgrid-provider</p></div>
issue:
- severity: fatal
  code: invalid
  diagnostics: "'provider' parameter id is not provided. Should be one of: smtp-provider, postmark-provider, mailgun-provider, sendgrid-provider"
```
{% endtab %}
{% endtabs %}

## Render a Questionnaire or response - $render

Renders a [Questionnaire](https://hl7.org/fhir/R4/questionnaire.html) or [QuestionnaireResponse](https://hl7.org/fhir/R4/questionnaireresponse.html) through a print template and returns HTML or PDF.

How-to: [Liquid print templates](../aidbox-ui-builder-alpha/printing-forms/liquid-templates.md). Language: [Liquid template language](liquid-template-language.md). The older Selmer / `SDCPrintTemplate` path is documented under [Template-based PDF generation](../aidbox-ui-builder-alpha/printing-forms/template-based-pdf-generation.md).

{% hint style="warning" %}
These paths are **not** under `/fhir`. `POST /fhir/QuestionnaireResponse/$render` returns 404.
{% endhint %}

### URLs

```
POST [base]/Questionnaire/[id]/$render
POST [base]/QuestionnaireResponse/[id]/$render
POST [base]/QuestionnaireResponse/$render
```

Under multi-tenancy the same operations exist as `/Organization/[org-id]/aidbox/…`.

### Parameters

{% hint style="warning" %}
NOTE: All parameters wrapped with `Parameters object`

```yaml
resourceType: Parameters
parameter:
- name:  [var-name]
  value: [var-value]
```
{% endhint %}

| Parameter | Cardinality | Type | Description |
| --- | --- | --- | --- |
| `purpose` | 0..1 | string | Pick the Library the form links for this purpose. Ignored if `template-id` is present. |
| `format` | 0..1 | string | `"pdf"` → PDF. Anything else or unset → HTML. |
| `liquid-template-id` | 0..1 | string | Render this `Library` explicitly. Wins over `purpose`. |
| `template-id` | 0..1 | string | Render this `SDCPrintTemplate` (Selmer). If both engines resolve, Selmer wins. |
| `questionnaire` | 0..1 | Questionnaire | Inline form — QuestionnaireResponse operation only. |
| `questionnaire-response` | 0..1 | QuestionnaireResponse | Inline response — QuestionnaireResponse operation only. |
| `repeated-items-count` | 0..1 | integer | Default `1`. Selmer + Questionnaire only; ignored by Liquid. |

With no selector, the operation reports a missing template.

#### purpose

```yaml
name: purpose
valueString: print
```

#### format

```yaml
name: format
valueString: pdf
```

#### liquid-template-id

```yaml
name: liquid-template-id
valueString: liquid-template-d0TSpgNl
```

### Examples

```yaml
POST /QuestionnaireResponse/qr-1/$render
Content-Type: text/yaml

resourceType: Parameters
parameter:
  - name: purpose
    valueString: print
  - name: format
    valueString: pdf
```

```yaml
POST /Questionnaire/q-1/$render
Content-Type: text/yaml

resourceType: Parameters
parameter:
  - name: liquid-template-id
    valueString: liquid-template-d0TSpgNl
```

### Responses

| Status | Meaning |
| --- | --- |
| 200 | Rendered. `format=pdf` → `application/pdf`. Otherwise the HTML string. |
| 404 | Form, response, or template not found. Body is an `OperationOutcome`. |
| 422 | HTML rendered, but PDF conversion failed. |

### PDF notes

`$render` with `format=pdf` converts HTML via openhtmltopdf. Relative URLs for images and CSS do not resolve — inline or absolute them. Prefer tables over flexbox/grid; no JavaScript, webfonts, or external images.
