Skip to main content
{
  "id": "whk_cSGjA6d9N8y8R",
  "type": "payment.update",
  "url": "https://reference.example.app/webhook",
  "metadata": null,
  "expand_event": false,
  "status": "active",
  "error": null,
  "created_at": "2020-12-09T00:41:05.647Z",
  "updated_at": "2020-12-09T00:41:05.647Z"
}
{
  "webhook_id": "whk_cSGjA6d9N8y8R",
  "id": "pmt_zR3nJG3c99P3X",
  "type": "payment.update",
  "op": "update",
  "path": "/payments/pmt_zR3nJG3c99P3X",
  "event": "evt_knqJgxKUnqDVJ",
  "refs": {
    "entity": "ent_qKNBB68bfHGNA"
  }
}
Webhooks allow the Method API to notify your application when certain events occur. To receive Webhook notifications, create a Webhook by registering a URL pointing to your application where triggered events should be sent to. This URL is where Method will send event information in an HTTPS POST request.

Handling webhooks

A Webhook event is considered successfully delivered when the corresponding URL endpoint responds with an HTTP status code of 200 within 5 seconds. If the criteria is not met, Method will reattempt 4 more times with each new attempt being delayed according to an exponential backoff algorithm, where the delay period between each attempt exponentially increases.
Webhooks that consistently fail to respond with a 200 will automatically be disabled.

Authentication

We use the auth_token and hmac_secret you provide to enable webhook authentication. There are 2 ways to authenticate an incoming request.
The token for request validation will be sent as a base64-encoded string in the Authorization header of the webhook. You can verify the request originated from Method by base64-encoding your auth_token and comparing it to the incoming header value to ensure the integrity of the event.
const SECRET = 'your-auth-token';
const expected_auth = Buffer.from(SECRET).toString('base64');
const incoming_auth = req.headers['authorization'];

if (incoming_auth !== expected_auth) {
  return res.status(401).send('Invalid Authorization token');
}
If you provide an hmac_secret when registering your webhook, Method will include a method-webhook-signature header in every request. This is an HMAC-SHA256 digest created using the hmac_secret as the shared secret, and computed over the string timestamp:payload, where timestamp is the value from the method-webhook-timestamp (UNIX timestamp in seconds) header (which is always included, even if no hmac_secret is provided) and payload is the raw request body. Checking for the timestamp freshness (5 min window) is optional, but recommended.
${method-webhook-timestamp}:${raw_payload}
The timestamp is always included in the method-webhook-timestamp header, even if no hmac_secret is provided.
const HMAC_SECRET = 'your-auth-token'; // Replace with the hmac_secret you provided Method
const ts = req.headers['method-webhook-timestamp'];
const hmac_sig = req.headers['method-webhook-signature'];
const msg = `${ts}:${req.rawBody}`;
const expected_sig = crypto.createHmac('sha256', HMAC_SECRET).update(msg).digest('hex');

if (!crypto.timingSafeEqual(Buffer.from(hmac_sig), Buffer.from(expected_sig))) {
  return res.status(401).send('Invalid signature');
}
import express from 'express';
import crypto from 'crypto';
import bodyParser from 'body-parser';

const app = express();

const SECRET = 'your-auth-token';
const HMAC_SECRET = 'your-hmac-secret'; // Replace with the hmac_secret you provided Method
const expected_auth = Buffer.from(SECRET).toString('base64');

app.use(bodyParser.json({
  verify: (req, _, buf) => req.rawBody = buf.toString(),
}));

app.post('/webhook', (req, res) => {
  const ts = req.headers['method-webhook-timestamp'];
  const hmac_sig = req.headers['method-webhook-signature'];
  const incoming_auth = req.headers['authorization'];

  if (!ts || !sig || !incoming_auth) return res.status(400).send('Missing headers');
  if (incoming_auth !== expected_auth) return res.status(401).send('Invalid Authorization token');
  // 1. check for freshness of the request
  if (Math.abs(Date.now() / 1000 - ts) > 300) return res.status(400).send('Timestamp expired');

  // 2. message reconstruction
  const msg = `${ts}:${req.rawBody}`;
  // 3. compute HMAC 
  const expected_sig = crypto.createHmac('sha256', HMAC_SECRET).update(msg).digest('hex');

  // 4. constant-time comparison
  if (!crypto.timingSafeEqual(Buffer.from(hmac_sig), Buffer.from(expected_sig))) {
    return res.status(401).send('Invalid signature');
  }

  res.status(200).send('Accepted');
});

app.listen(3000, () => console.log('Listening on port 3000'));

Webhook Objects

id
string
Unique identifier for the Webhook.
type
enum
The event type to be sent to this URL. See Webhook Event Types.
url
string
The URL receiving the webhook event.
metadata
object | null
Additional data provided during creation. See metadata
expand_event
boolean
Whether to expand the event object in the Webhook payload. (default: false)
status
enum
A status indicating the status of the Webhook.
active
The Webhook is active and will receive events.disabled
The Webhook is temporarily disabled and will not receive events.requires_attention
The Webhook has entered a state where it requires attention and will continue to receive events.
error
object | null
An object representing an error that occurred while processing this Webhook. See Webhook errors.
created_at
string
Timestamp of when the Webhook was created.
updated_at
string
Timestamp of when the Webhook was last updated.

Webhook event object

id
string
The identifier of the resource modified.
type
string
The event type. See Webhook Event Types.
path
string
The URL path pointing to the affected resource. Make a HTTP GET request to this path to retrieve the resource.

Webhook Event Types

NameDescription
account.createA new Account was successfully created.
account.updateAn Account has been updated.
account.openedAn Account has been opened.
account.closedAn Account has been closed.
sensitive.createA new Sensitive was successfully created.
sensitive.updateAn Sensitive has been updated.
account_verification_session.createA new AccountVerificationSession was successfully created.
account_verification_session.updateAn AccountVerificationSession has been updated.
balance.createA new Balance was successfully created.
balance.updateA Balance has been updated.
card_brand.createA new CardBrand was successfully created.
card_brand.updateA CardBrand has been updated.
card_brand.availableA CardBrand has been made available.
connect.createA new Connect was successfully created.
connect.updateA Connect has been updated.
connect.availableAn Async Connect has completed.
credit_score.createA new CreditScore was successfully created.
credit_score.updateA CreditScore has been updated.
entity.createA new Entity was successfully created.
entity.updateAn Entity has been updated.
entity_verification_session.createA new EntityVerificationSession was successfully created.
entity_verification_session.updateAn EntityVerificationSession has been updated.
identity.createA new Identity was successfully created.
identity.updateAn Identity has been updated.
payment.createA new Payment was successfully created.
payment.updateA Payment has been updated.
payment_reversal.createA new PaymentReversal was successfully created.
payment_reversal.updateA PaymentReversal has been updated.
payoff.createA new Payoff was successfully created.
payoff.updateA Payoff has been updated.
product.createA new Product was successfully created.
product.updateA Product has been updated.
report.createA new Report was successfully created.
report.updateA Report has been updated.
subscription.createA new Subscription was successfully created.
subscription.updateA Subscription has been updated.
transaction.createA new Transaction was successfully created.
transaction.updateA Transaction has been updated.
update.createA new Update was successfully created.
update.updateAn Update has been updated.
credit_score.increasedA user’s credit score has increased.
credit_score.decreasedA user’s credit score has decreased.
attribute.createA new Attribute was successfully created.
attribute.credit_health_credit_card_usage.increasedCredit card usage has increased.
attribute.credit_health_credit_card_usage.decreasedCredit card usage has decreased.
attribute.credit_health_derogatory_marks.increasedNumber of derogatory marks increased.
attribute.credit_health_derogatory_marks.decreasedNumber of derogatory marks decreased.
attribute.credit_health_hard_inquiries.increasedNumber of hard inquiries increased.
attribute.credit_health_hard_inquiries.decreasedNumber of hard inquiries decreased.
attribute.credit_health_total_accounts.increasedTotal number of accounts increased.
attribute.credit_health_total_accounts.decreasedTotal number of accounts decreased.
attribute.credit_health_credit_age.increasedCredit age has increased.
attribute.credit_health_credit_age.decreasedCredit age has decreased.
attribute.credit_health_payment_history.increasedPayment history score has improved.
attribute.credit_health_payment_history.decreasedPayment history score has decreased.
method_jwk.createA new Method JWK (public key) was successfully created.
method_jwk.updateA Method JWK has been updated (deprecated or disabled).
{
  "id": "whk_cSGjA6d9N8y8R",
  "type": "payment.update",
  "url": "https://reference.example.app/webhook",
  "metadata": null,
  "expand_event": false,
  "status": "active",
  "error": null,
  "created_at": "2020-12-09T00:41:05.647Z",
  "updated_at": "2020-12-09T00:41:05.647Z"
}
{
  "webhook_id": "whk_cSGjA6d9N8y8R",
  "id": "pmt_zR3nJG3c99P3X",
  "type": "payment.update",
  "op": "update",
  "path": "/payments/pmt_zR3nJG3c99P3X",
  "event": "evt_knqJgxKUnqDVJ",
  "refs": {
    "entity": "ent_qKNBB68bfHGNA"
  }
}
I