Webhooks
Receive a signed ReWords.AI webhook when an edit finishes, and verify the X-Rewords-Signature HMAC in Node.js or Python.
Pass callback_url in POST /edits. When the job reaches succeeded or failed, we POST the same JSON you would
get from GET /edits/{id} to that URL.
callback_urlmust be anhttpsURL. Its query string is kept, so you can add your own reference, such ashttps://example.com/hooks/rewords?order=1234.- Reply with any
2xxstatus within 10 seconds to acknowledge it. Anything else — another status, a timeout, a connection error — counts as failed. - A failed delivery is retried 1, 5 and 30 minutes after the previous attempt, so the last retry comes about 36
minutes after the first. After that we stop;
GET /edits/{id}still returns the result. - Every retry sends the same bytes with the same signature. Because of retries, you can receive the same job more
than once: use the edit
idto ignore one you have already handled. - Each delivery is signed:
X-Rewords-Signature: sha256=<64 hex characters>, an HMAC-SHA256 of the raw request body using your webhook secret. Use the whole secret string, including itswhsec_prefix, as the HMAC key.
Your webhook secret
There is one webhook secret per account, shared by all of its keys. Find it on your API keys page. You can reset it there; the old secret stops working at once, so update your receiver right after resetting.
Verify the signature
Reject the request with 401 if verification fails, and do not process it. Re-sending cannot fix a bad signature.
import crypto from 'node:crypto';
export function verify(rawBody, header, secret) {
if (typeof header !== 'string') return false;
const expected = Buffer.from('sha256=' + crypto.createHmac('sha256', secret).update(rawBody).digest('hex'));
const received = Buffer.from(header);
return received.length === expected.length && crypto.timingSafeEqual(received, expected);
}import hashlib, hmac
from typing import Optional
def verify(raw_body: bytes, header: Optional[str], secret: str) -> bool:
if not header:
return False
expected = "sha256=" + hmac.new(secret.encode(), raw_body, hashlib.sha256).hexdigest()
return hmac.compare_digest(header.encode(), expected.encode())Verify against the raw bytes before parsing the JSON — re-serialising it changes the signature. In Express, for
example, read the body with express.raw({ type: 'application/json' }).