curl -d @file strips newlines: a Sume webhook test that fails 401
Signing a fixture file and sending it with curl -d changes the bytes. Use --data-binary to test a Sume webhook receiver. Both signatures shown.

You sign fixture.json with openssl, post it with curl -d @fixture.json, and your receiver says the signature is wrong. The receiver is fine. curl -d @file strips carriage returns and newlines from the file contents, so the body that arrives is not the body you signed. Sume signs the exact raw bytes (<timestamp>.<raw_body>), and one missing trailing newline is a different HMAC.
On curl 8.7.1, a 45-byte fixture ending in a newline arrived as 44 bytes with -d @fixture.json and as 45 bytes with --data-binary @fixture.json.
| Body signed | Bytes | Signature (hex, timestamp 1790000000, secret whsec_test_123) |
|---|---|---|
| With trailing newline | 45 | 9d95a169d5504e9d88aa0e1fe205e082b6a7fdc2bdfc6336831afdd5e565f83f |
| Without it | 44 | 5ba7a215439d4b856d34fa60b0bd467778efa0d98eca14f659fcb5e74b1ca1f1 |
A sign-and-send script that works
This hashes the file bytes with cat, then sends the same file with --data-binary. Pointed at a Go receiver that verifies the header, it returned 200; swapping --data-binary for -d returned 401. Use a throwaway secret and your own endpoint; never put a real signing secret in a script that gets committed.
#!/usr/bin/env bash
set -eu
: "${SECRET:?set SECRET}"
TS=1790000000
SIG=$(printf '%s.' "$TS" | cat - fixture.json | openssl dgst -sha256 -hmac "$SECRET" | sed 's/.*= //')
curl -s -o /dev/null -w '%{http_code}\n' --data-binary @fixture.json \
-H "x-sume-webhook-timestamp: $TS" -H "x-sume-webhook-signature: sume-v1=$SIG" \
"http://localhost:8894/hook"Related traps
jq . and jq -c rewrite whitespace, so pipe through them before signing, not after. Editors that add a final newline on save will change a fixture without telling you. When a mismatch is baffling, print the byte count on both sides (wc -c here, len(body) there) before suspecting the secret.
On the receiving side, verify against the raw request bytes, before any JSON parser re-serializes them. The related posts cover that for Node, Go and Bun.
Sources
Related posts
More in Developers
- curl --json: submit a Wan 3.0 video to Sume in one flag
curl 7.82 and later has --json, which sets the body and the JSON headers for you. A Wan 3.0 submit with an Idempotency-Key, then a status poll with jq.
- curl -K config file: keep your Sume API key out of ps output
A key passed with -H shows in ps args and shell history. curl -K reads headers from a chmod 600 file instead. Tested on curl 8.7.1 against a stub.
- Cursor project .cursor/mcp.json or global ~/.cursor/mcp.json for Sume?
Put a URL-only Sume entry in .cursor/mcp.json to share it with a repo, and keep any API key in an env variable in the global file, not in git.
- Cut a 10-minute recording into 3-minute Reels with video trim
A 600 s recording becomes four trims: 180, 180, 180 and 60 s, $0.08 in total. Python loop with a safe Idempotency-Key per segment; run it with an imported file.
Written by Sume