Each app has one HTTPS endpoint. GramPayBot sends JSON POST requests for invoice and billing events.

Configure the URL

In the bot open the app and choose 🪝 Webhooks → Configure URL. The same screen has Send test webhook, Rotate secret, Delivery logs, Enable and Disable. The webhook secret is separate from the API token and is shown once.

Headers

HeaderValue
Content-Typeapplication/json
GramPay-EventEvent name
GramPay-Delivery-IDUnique delivery ID
GramPay-TimestampSend time in ISO 8601
GramPay-Signaturesha256=<hex>
POST /webhook/<app_id>200 OK
  • GramPay-Eventinvoice_paid
  • GramPay-Signaturesha256=8f21…d491
{
  "update_type": "invoice_paid",
  "payload": {
    "public_id": "GU91XHr8AUNQ8l9r",
    "status": "paid",
    "amount_usd": "180.00",
    "paid_token": "usdt",
    "tx_hash": "8f21…d491"
  }
}
✓ invoice_paid

Verify the signature

Compute HMAC-SHA256 over the exact raw request bytes with the webhook secret, prefix the lowercase hex digest with sha256=, and compare in constant time.

const expected = 'sha256=' + crypto.createHmac('sha256', secret).update(rawBody).digest('hex');
const valid = given.length === expected.length &&
  crypto.timingSafeEqual(Buffer.from(given), Buffer.from(expected));

Reject missing or invalid signatures with a non-2xx response. Do not parse and reserialize JSON before calculating HMAC.

Three-request setup audit

Saving or testing a URL sends the same webhook_test body three times: unsigned, incorrectly signed and correctly signed. The valid request must return 2xx within 5 seconds. Phoenix does not follow redirects, so register the exact URL. Accepting invalid probes does not block setup but produces a security warning.

Your handler must accept webhook_test without business logic. Setup probes are synchronous and are not retried.

Delivery and retries

Only HTTP 2xx succeeds. Real events are retried up to 17 times over about four days with increasing delays. After the last failure, the endpoint becomes disabled_by_failures, the owner is notified, and it must be enabled again after the problem is fixed.

Handler rules

  • Verify and persist the event, return 2xx quickly, then process asynchronously.
  • Deduplicate by GramPay-Delivery-ID or an event-specific stable key.
  • Make fulfilment idempotent; a retry must not grant the product twice.
  • Log and acknowledge unknown event types so future additions do not cause retry storms.

See events and payloads for the two body shapes.

Useful next step

Your first 50 confirmed payments are on us.

Start with $5 credit ↗