ReWords.AI API

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_url must be an https URL. Its query string is kept, so you can add your own reference, such as https://example.com/hooks/rewords?order=1234.
  • Reply with any 2xx status 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 id to 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 its whsec_ 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.

Node.js
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);
}
Python 3.8+
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' }).

On this page