Skip to main content
Use this when a watch run can deliver more records than your receiver will accept in one request, or when you would rather load a run as one file than reassemble it from several requests. A watch run can deliver its records two ways. By default they ride inside the notification. Set config.payload_delivery_type to "link" and the notification carries a pre-signed S3 URL instead, pointing at one file that holds every record of that run. link is also what raises the per-run cap: inline delivery stops at 1,000 records per run, and link carries up to 10,000. This page walks you through the basics: the two modes, how to set each one, what the notification looks like, and how to read the file, then folds in worked recipes you can copy, paste, and adapt.
This is available on every watch: person, company, and job discovery watches, and person and company entity watches. It applies on create and on PATCH.

Request body

payload_delivery_type is a single key inside a watch’s config. Everything else about the watch stays as it was.

Response body

The notification carries metadata.payload_delivery on every delivery, inline and link alike.
An inline delivery carries { "type": "inline" } with no other keys, so read type before you reach for url. A link watch can still hand you an inline body if the file could not be written.

Rate limits and credits

Pricing: neither mode changes what a watch costs. You pay per record delivered, at the same rate either way, and reading the file costs no credits.
  • Rate limit: changing the mode is a watch-management request, 10 per minute on that watch path. See Rate limits.
  • Link lifetime: 5 days from the moment the run issued it.

Examples

Set it on create, switch an existing watch, stream the file.

The two modes

What changes, and what stays the same.

Watchers

The watches this applies to.

Why you would switch

A run above 1,000 records needs link. config.max_results_per_run accepts 1 to 10000, and inline delivery stops at 1000. An inline run posts its chunks one after another, so a 10,000-record run would become thousands of sequential requests. A link run is always one request, whatever the run holds. The API rejects a cap above 1000 on an inline watch, on create and on PATCH. Most receivers cap how large a request body they accept, and a body over the cap is refused whole. An AWS Lambda proxy integration stops at 6 MB, an ALB target at 1 MB. A run that delivers 1,000 enriched people can pass either of those. Inline delivery splits a large run across several requests to stay under a receiver’s cap. Link delivery sidesteps the cap instead. The body carries no records, so its size does not grow with the run. Pick link if your receiver has a small body cap, or if you would rather load a run as one file than reassemble it from several requests.

The two modes

Both modes carry the same metadata, including every count in summary, so your record counts do not move. Only the records themselves change places.
On a link delivery the body has no results key at all. Read metadata.payload_delivery.type and branch on it rather than testing for the presence of results.

The file

One file per run, in NDJSON: one complete JSON object per line, newline terminated, with no wrapping array. You can stream it line by line without holding the whole run in memory. Each line is the same record the inline body would have carried, with no container around it.
A discovery watch’s lines are the raw dataset records, without the { "added": [ … ] } wrapper the inline body uses, because a file of lines needs no wrapper. An entity watch’s lines are the same { changes, record } objects the inline body carries.
The URL is signed for 5 days. Download the file when the notification arrives rather than storing the URL for later. If you need the run again after that, the run summary issues a fresh link over the same records.
A run writes its file once, before it fans out. A webhook, a Slack message, a Google Chat message, and an email from the same run all carry the same link to the same bytes. Slack, Google Chat, and email messages already show a bounded preview of a run rather than every record. On a link watch those messages keep their preview and gain the link, so a reader who wants the records the preview left out has the full file.

Reading a past run

The run-summary endpoint follows the watch’s mode too.
On a link watch the response carries a top-level payload_delivery block, and each entry in notifications keeps its delivery outcome but drops the records:
On an inline watch, payload_delivery is { "type": "inline" } and notifications[].payload carries the records as before. Reading a run hands you the same file the run delivered, so the bytes match what your channel received. A watch created with no channels never pushed anything, so the first read writes the file and links it. A run that delivered nothing offers no link. A SKIPPED or FAILED run reports { "type": "inline" } with an empty notifications list.

Inline is never overridden

A watch left on inline posts its records at any size, up to its cap of 1,000 per run. Delivery does not switch a large run to a link on its own, because the mode is your choice and a silent switch would change the body shape your receiver parses. A large inline run is split instead: records are grouped into requests of at most ~1 MB or 100 records, and each request carries its own notification_id (ntf_{run_id}_{index}) and a metadata.chunk.index. A run that fits in one request keeps the plain ntf_{run_id} id and no chunk key, so a small run looks exactly as it always has. If your receiver rejects an oversize body, that is the signal to move the watch to link.

If the file cannot be written

Writing the file can fail. When it does, the run falls back to delivering the records inline and says so: metadata.payload_delivery reports { "type": "inline" } and the body carries results as usual. The delivery still reaches you, without the link. This is the reason to branch on metadata.payload_delivery.type rather than on the watch’s configured mode. A link watch can still hand you an inline body.

Test sends

A test send on a link watch to a webhook mirrors a real delivery: the body carries metadata.payload_delivery with a link and no results, alongside the usual metadata.test: true. The file holds the sample records. Each test preview writes its own file rather than reusing the run’s, so a preview never overwrites the records of a real run.

Errors

A value other than "inline" or "link" is rejected on create and on PATCH:
400 Bad Request
The check is case sensitive, so "LINK" is rejected. Omitting the key, or sending it as null, means inline. A max_results_per_run above 1000 is rejected while delivery is inline:
400 Bad Request
The rule reads both keys together, so it fires when you leave payload_delivery_type out as well. The default is inline. On a PATCH the rule is checked against the watch as it will be, not against the body alone:
  • Raising the cap above 1000 on a link watch is accepted.
  • Raising it on an inline watch is rejected.
  • Sending the new cap and "link" in the same PATCH is accepted.
  • Switching a watch that already carries a cap above 1000 back to inline is rejected. Lower the cap first.
A cap above 10000 is out of range in either mode:
400 Bad Request
A config PATCH is validated as a whole block, so repeat the watch’s current trigger alongside the key you are changing. Leave it out and both halves of the missing trigger come back at once:
400 Bad Request
The keys you do send are merged into the stored config rather than replacing it, so anything you omit keeps its current value. Both responses use the shared watcher error envelope, which is documented in full under Error handling.

Examples

Worked recipes you can copy, paste, and adapt. Each one is a full working request. Swap the notification channel for your own.
The same PATCH, with the other value. Sending null or omitting the key entirely also means inline.
Request
Read metadata.payload_delivery.type rather than testing for results. A link watch falls back to an inline body when the file cannot be written, so both paths have to work.
Fetch the file with a plain GET. The URL carries its own authentication in the query string, so send no authorization header.
NDJSON streams line by line, so a large run never has to be held in memory. jq -c on the stream works the same way for one record or for a hundred thousand.

Pricing

Neither mode changes what a watch costs. You pay per record delivered, at the same rate either way, and reading the file costs no credits. See Pricing for the per-watch rates.

What to do next