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 carriesmetadata.payload_delivery on every delivery, inline and link alike.
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 needslink. 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.
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.{ "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.
Every channel gets the same link
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 alink 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.link watch the response carries a top-level payload_delivery block, and each entry in notifications keeps its delivery outcome but drops the records:
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 oninline 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 alink 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
"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
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
1000on alinkwatch is accepted. - Raising it on an inline watch is rejected.
- Sending the new cap and
"link"in the samePATCHis accepted. - Switching a watch that already carries a cap above
1000back toinlineis rejected. Lower the cap first.
10000 is out of range in either mode:
400 Bad Request
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
Examples
Worked recipes you can copy, paste, and adapt. Each one is a full working request. Swap the notification channel for your own.Set link delivery on a new discovery watch
Set link delivery on a new discovery watch
Raise The response above is trimmed to the keys under discussion. A real create also echoes
max_results_per_run and set the mode in the same config block. A run that now delivers up to 10,000 people still arrives as one small request. A cap this high needs "link", so the two keys travel together.realtime_filters, narrowing_filters, sorts, fields, and last_notified_at, all null on this watch.Set link delivery on a new entity watch
Set link delivery on a new entity watch
The same key works on an entity watch, where each line of the file is a
{ changes, record } object rather than a raw record.Request
Switch an existing watch to link
Switch an existing watch to link
payload_delivery_type is one of the mutable config keys, alongside the schedule and the result cap. Changing it leaves the watch’s filters, tracked fields, and sort order exactly as they were. The change takes effect on the next run; runs already delivered keep whatever they were delivered as.Switch a watch back to inline
Switch a watch back to inline
The same
PATCH, with the other value. Sending null or omitting the key entirely also means inline.Request
Branch on the delivery mode in your receiver
Branch on the delivery mode in your receiver
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.Stream the file from the command line
Stream the file from the command line
Fetch the file with a plain NDJSON streams line by line, so a large run never has to be held in memory.
GET. The URL carries its own authentication in the query string, so send no authorization header.jq -c on the stream works the same way for one record or for a hundred thousand.Re-read a past run and get a fresh link
Re-read a past run and get a fresh link
The signed URL expires after 5 days. The run summary issues a new one over the same records, so a run is never lost to an expired link.A watch created with no channels never pushed anything, so the first read is what writes the file and links it.
Request
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
- Set it on a watch: see Company discovery, Person discovery, or the Job watcher.
- Set it on an entity watch: see Company entity or Person entity.
- Check what a run delivered: read the run summary, which follows the same mode.

