# Fill templates from your data (/docs/guides/tutorials/fill-templates-from-your-data)


When your system already knows what goes in a document, such as a customer's name, a policy number, or a start date, you can send those values with the envelope instead of matching them to field names yourself. You give each field a data key once, and from then on every send passes your data as it is, and we place each value in every field that uses its key.

## Give fields a data key [#give-fields-a-data-key]

A data key is your name for a value, such as `applicant.full_name`. Keys use letters, digits, `_`, and `-`, with `.` between levels, up to 255 characters. Text, date, checkbox, radio, and dropdown fields can have one, and several fields can share a key when they show the same value (a name repeated on every page, for example).

You set `data_key` when you [create](/docs/reference/Rest-API/api-docs/fields/createField) or [update](/docs/reference/Rest-API/api-docs/fields/updateField) a template field, and the template builder sets it from the "Fill with" menu in the field's Inspector panel. For documents you send without a template, a text tag's `k:` option sets it, as in `{{textbox:Applicant;k:applicant.full_name}}` (see [Tagging dynamic documents](/docs/guides/tutorials/tagging-dynamic-documents)).

## Send your data [#send-your-data]

Pass your data as `prefill_data` when you [create the envelope](/docs/reference/Rest-API/api-docs/envelopes/createEnvelope). Nested objects and dotted keys mean the same key, so these two requests fill the same fields:

```typescript
import {createEnvelope} from '@verdocs/js-sdk';

const envelope = await createEnvelope(endpoint, {
  template_id: templateId,
  recipients: [{role_name: 'Applicant', first_name: 'Jordan', last_name: 'Rivera', email: 'jordan@example.com'}],
  prefill_data: {
    applicant: {full_name: 'Jordan Rivera', state: 'TX'},
    policy: {effective_date: '2026-11-01', premium: 1234.5, coverage: {flood: true}},
  },
});

// Same thing, flat
const flat = {'applicant.full_name': 'Jordan Rivera', 'applicant.state': 'TX', 'policy.effective_date': '2026-11-01'};
```

Each kind of field takes its value a little differently:

| Field    | Send                                                                                                                                                                                                                                                                                   |
| -------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Text     | Text, a number, or true/false. A number in a plain text field shows with grouping and two decimals ("1,234.50"). A [number field](/docs/guides/tutorials/formatting-text-fields) stores the number itself, and also accepts text written its locale's way, like "12.450,50" for de-DE. |
| Date     | An ISO 8601 date, like `2026-11-01`                                                                                                                                                                                                                                                    |
| Checkbox | `true` or `false` (we also take "yes", "no", 1, and 0)                                                                                                                                                                                                                                 |
| Dropdown | The option's id or its label                                                                                                                                                                                                                                                           |
| Radio    | The label or name of the option to select                                                                                                                                                                                                                                              |

When you also send a `fields` entry for a field, that entry wins, so you can still override one placement while the rest come from your data.

## See what happened [#see-what-happened]

Whenever you send `prefill_data`, the response includes a `prefill_result`. It lists keys by name only, never their values, so it's safe to log.

```json
{
  "placements_filled": 6,
  "unused_keys": ["applicant.ful_name"],
  "missing_keys": ["policy.coverage.flood"],
  "errors": [{"key": "applicant.state", "field": "state", "message": "No dropdown option matches this value"}],
  "format_problems": [{"key": "applicant.email", "field": "email", "message": "Not a valid email"}],
  "hints": [{"key": "applicant.ful_name", "did_you_mean": "applicant.full_name"}]
}
```

`unused_keys` are keys you sent that no field uses, and `hints` suggest the key you most likely meant. `missing_keys` are keys on the fields that you didn't send. `errors` are values that couldn't go in their field at all, such as "maybe" for a checkbox, so those fields keep their default.

`format_problems` are values we did place, but that don't fit the field's format, number range, input mask, pattern, or character limit (the checks in [Formatting text fields](/docs/guides/tutorials/formatting-text-fields)), or a date field's allowed dates. The signer has to correct those before they can finish, which they can't do on a read-only field, so these are worth fixing at the source.

## Strict mode [#strict-mode]

Set `prefill_options: {strict: true}` when you'd rather we refuse a send than let a mismatch through. We return a 400 with the same `prefill_result`, and nothing is created or sent, when a key goes unused, a value has an error or a format problem, or you left out the key for a read-only field. Leaving out a key for a field the signer can fill in is fine, since they'll fill it in themselves.

Strict mode pairs well with `dry_run: true`, which runs every check a real send makes and returns the value each field would get, without creating anything. js-sdk's `previewEnvelope` wraps it. Dry runs need a `template_id`.

## Bulk sends [#bulk-sends]

`POST /v2/envelopes/bulk` takes `prefill_data` on each entry, so every envelope in the batch gets its own data, and `prefill_options` once for the whole batch. The response adds `prefill_results`, one per entry in the same order, with `null` for entries that didn't send any data. In strict mode we check every entry before any envelope goes out, so one bad row stops the batch instead of leaving it half sent.

```json
{
  "template_id": "d2338742-f3a1-465b-8592-806587413cc1",
  "prefill_options": {"strict": true},
  "entries": [
    {"recipients": [{"role_name": "Applicant", "first_name": "Jordan", "last_name": "Rivera", "email": "jordan@example.com"}], "prefill_data": {"applicant": {"full_name": "Jordan Rivera"}}},
    {"recipients": [{"role_name": "Applicant", "first_name": "Sam", "last_name": "Lee", "email": "sam@example.com"}], "prefill_data": {"applicant": {"full_name": "Sam Lee"}}}
  ]
}
```

## Sends without a template [#sends-without-a-template]

`prefill_data` works the same way when you [send an envelope without a template](/docs/guides/tutorials/send-an-envelope-without-a-template). We match it against the `data_key` on the fields in your request and on the `k:` options in your documents' text tags. A field that already has a default, from your request or from its tag, keeps it.

## Know what a template expects [#know-what-a-template-expects]

[`getTemplateDataContract`](/docs/reference/Rest-API/api-docs/templates/getTemplateDataContract) lists every key a template reads, with its type, how many fields use it, its options, and whether it's needed (a read-only field the signer can't fill in). It also returns a JSON Schema for the template's `prefill_data`, which you can use to check your payloads before you send them, and a `contract_hash` that changes only when the keys, their types, their options, or whether they're needed do. Storing that hash in a CI check is an easy way to hear about a template change before it reaches your sends.

```typescript
import {getTemplateDataContract} from '@verdocs/js-sdk';

const {keys, json_schema, contract_hash} = await getTemplateDataContract(endpoint, templateId);
```
