How to convert webhook json payloads to a csv log
- Step 1Collect the webhook payloads — Use a single saved event, a JSON array of events, or a logfile with one event per line (NDJSON). Drop the file onto the converter above. Free files cap at 2 MB.
- Step 2Pick the input format — Auto detect handles a single event or array and falls back to NDJSON for line-delimited logs. Force NDJSON / JSONL when each line is one delivery — that is the most reliable for log files.
- Step 3Keep flattening on — Webhook payloads nest the real data under
data.object. Flatten nested objects (default) turnsdata.object.id,data.object.amount, etc. into individual columns. - Step 4If the file is one big array under a key, extract it first — If you stored events as
{ "events": [...] }, the top level is all-arrays and converts directly. If it's{ "events": [...], "meta": {...} }, extract$.eventsfirst with json-path-extractor. - Step 5Convert and scan event types — Click Convert to CSV. Sort by the
typecolumn in your spreadsheet to see event-type distribution. Records in should match your delivery count. - Step 6Download for analysis — Excel-Ready CSV (BOM + CRLF) for spreadsheet pivot tables; plain CSV (LF) for grep / awk over the log.
Webhook envelope fields after flattening
How a typical provider webhook envelope maps to CSV columns.
| Webhook field | CSV column | Note |
|---|---|---|
id | id | Event delivery id |
type | type | Event name, e.g. charge.succeeded — sort on this |
created | created | Often a Unix epoch — kept verbatim |
data.object.id | data.object.id | The underlying resource id, flattened |
data.object.amount | data.object.amount | Provider-specific units preserved exactly |
Choosing the input format for webhook logs
Match the format to how your deliveries were stored.
| Stored as | Input format | Result |
|---|---|---|
| One event per line | NDJSON / JSONL | One CSV row per delivery |
[ {event}, {event} ] | JSON array or Auto detect | One row per event |
| Single captured event | Auto detect | A one-row CSV |
{ "events": [...] } | Auto detect | All-array top level → rows from events |
Cookbook
Real webhook payload shapes and the log CSV the converter produces. Customer data anonymised.
NDJSON delivery log to a CSV table
ExampleEach captured delivery is one line; the converter flattens the envelope into a sortable log table.
Input (deliveries.ndjson):
{"id":"evt_1","type":"charge.succeeded","data":{"object":{"id":"ch_1","amount":4900}}}
{"id":"evt_2","type":"charge.refunded","data":{"object":{"id":"ch_1","amount":4900}}}
Output CSV:
id,type,data.object.id,data.object.amount
evt_1,charge.succeeded,ch_1,4900
evt_2,charge.refunded,ch_1,4900Mixed event types in one log
ExampleDifferent event types carry different payloads; the union header keeps every column present with empties where unused.
Input:
{"type":"charge.succeeded","data":{"object":{"amount":4900}}}
{"type":"customer.updated","data":{"object":{"email":"a@x.com"}}}
Output CSV:
type,data.object.amount,data.object.email
charge.succeeded,4900,
customer.updated,,a@x.comEvents stored under a top-level key
ExampleAn all-arrays top level ({ "events": [...] }) converts directly to one row per event, no extraction needed.
Input:
{ "events": [
{"type":"order.created","data":{"object":{"id":"o_1"}}},
{"type":"order.paid","data":{"object":{"id":"o_1"}}}
] }
Output CSV:
type,data.object.id
order.created,o_1
order.paid,o_1Isolating a failed delivery
ExampleA truncated write left a bad line; the error names it so you can drop one corrupt delivery and re-run.
Input:
{"id":"evt_1","type":"ping"}
{"id":"evt_2","type": <-- truncated
Result:
Error: Invalid JSON on line 2 — Unexpected end of JSON inputPayload array kept as one cell
ExampleA list field in the payload stays in one cell; choose comma-joining for a readable log column.
Input:
{"type":"order.created","data":{"object":{"items":["sku-1","sku-2"]}}}
Array values = Comma joined:
type,data.object.items
order.created,"sku-1, sku-2"Errors and edge cases
Real errors and silent failures sourced from each platform's own documentation. Match the wording to the row, fix what the row says to fix.
Different event types have different fields
SupportedThe union-of-keys header gives every payload field a column across all event types, leaving empties where a type does not use it. This is exactly what you want for a mixed delivery log.
One corrupt line in an NDJSON log
Invalid JSONA single malformed delivery line aborts conversion with Invalid JSON on line N. Remove or repair that line — NDJSON parsing here is all-or-nothing.
Timestamps stay as Unix epochs
By designThe converter does not interpret created. Epoch integers are preserved verbatim — convert them to readable dates in your spreadsheet.
Envelope wraps events with scalar metadata
By designIf your file is { "events": [...], "meta": {...} }, not all top-level values are arrays, so it collapses to one wide row. Extract $.events first with json-path-extractor.
Payload contains a nested array of objects
Not expandedAn array of objects (e.g. line_items) stays in one cell as JSON. For a row per item, extract that path and convert it separately.
Signature / verification fields present
PreservedThe converter does not verify signatures — it simply flattens any signature or header fields present into columns. Verification must happen in your webhook handler, not here.
Log over the free 2 MB limit
BlockedFree conversions cap at 2 MB / 500 rows. High-volume webhook logs need Pro (100 MB / 100,000 rows) or splitting by date.
Empty or whitespace-only file
ExpectedYields zero rows and an empty CSV with no error. Confirm your capture actually wrote the payloads.
Frequently asked questions
How do I flatten the `data.object` part of a webhook?
Keep Flatten nested objects on (the default). data.object.id, data.object.amount, and so on become individual dot-notation columns automatically.
Can I convert a logfile with one event per line?
Yes. That is NDJSON — use Auto detect or force NDJSON / JSONL. Each non-empty line becomes one CSV row.
Different webhook types have different fields — will the CSV align?
Yes. The header is the union of all keys across all events, so every payload field gets a column, with empty cells where an event type doesn't use it.
Does the tool verify webhook signatures?
No. It only flattens the JSON. Any signature fields present are kept as columns, but verification must happen in your webhook handler before you trust the data.
How do I turn the `created` timestamp into a date?
It is usually a Unix epoch, preserved as-is. Convert it in your spreadsheet — the converter does no timestamp interpretation.
My events are stored under a `events` key — do I need to extract it?
Only if other scalar fields sit beside it. If the top level is purely { "events": [...] }, it converts directly. If it's { "events": [...], "meta": {...} }, extract $.events first with json-path-extractor.
What happens to arrays inside a payload?
They go into one cell (JSON literal, pipe, or comma). Arrays of objects are not split into rows — extract the path separately if you need that.
Is my webhook data uploaded?
No. Parsing and conversion run in your browser. Payloads, which may contain customer data, never reach a server.
One delivery line is corrupt — what happens?
Conversion stops with Invalid JSON on line N, naming the bad line. Remove or fix it and re-run.
How large a webhook log can I convert?
Free: 2 MB / 500 rows. Pro: 100 MB / 100,000 rows. Split high-volume logs by date if needed.
Can I analyse Stripe webhooks specifically?
Yes — Stripe events follow the same envelope. For converting raw Stripe API exports (not webhooks), see the Stripe export guide.
Can I turn the CSV log back into JSON?
Yes, with csv-to-json, which can emit line-delimited JSON if you want to re-feed events into another system.
Privacy first
Conversion runs locally in your browser. No file is uploaded — only metadata counters are saved for signed-in dashboard stats.