Webhooks

Subscribe to events and MGX will POST a signed payload to your URL when they happen — so you do not have to poll. Requires the webhooks.read / webhooks.write scopes and a token bound to a team.

Create a subscription with the URL you want delivered to and the event types you care about. The SDKs expose the management endpoints (list/create/delete/deliveries) plus a verify() helper for the inbound side.

Create a subscription

const sub = await mgx.webhooks.create({
  url: 'https://erp.example.com/mgx/hook',
  events: ['trade.created', 'cashbid.offer_received'],
})
// The signing secret is returned only once — store it now.
console.log(sub?.id, sub?.secret)

Events

  • Name
    bid.accepted
    Type
    event
    Description
    A seller accepted your team's bid.
  • Name
    bid.rejected
    Type
    event
    Description
    Your bid was rejected or the lot delisted.
  • Name
    bid.countered
    Type
    event
    Description
    A seller countered your bid.
  • Name
    trade.created
    Type
    event
    Description
    A bid became a trade.
  • Name
    trade.settled
    Type
    event
    Description
    Both invoices on a trade were paid.
  • Name
    cashbid.offer_received
    Type
    event
    Description
    A seller offered against your cash bid.
  • Name
    inventory.matched
    Type
    event
    Description
    A new lot matched a saved search.

Verifying webhooks

Every delivery carries an MGX-Signature header of the form t=<unix>,v1=<hex hmac>, where the HMAC is SHA-256 over the string "{t}.{rawBody}" keyed with the subscription's signing secret — the whsec_... value returned once when the subscription is created. Store it then; MGX cannot show it again.

The SDKs ship a verify() that recomputes the HMAC and compares it in constant time, rejects timestamps outside a tolerance window (300s by default) to block replays, and returns a typed WebhookEvent ({ id, type, created_at, data }). Pass the exact raw request body — not a re-serialized object — or the signature will not match. On any failure it throws, so reject the delivery with a 400 and never act on an unverified payload.

Verify an inbound webhook

// e.g. Express — read the RAW body with express.raw({ type: 'application/json' }).
app.post('/mgx/hook', (req, res) => {
  const rawBody = req.body.toString('utf8')
  const signature = req.header('MGX-Signature') ?? ''
  try {
    const event = mgx.webhooks.verify(rawBody, signature, process.env.MGX_WEBHOOK_SECRET!)
    switch (event.type) {
      case 'trade.created':
        console.log('new trade', event.data)
        break
      case 'cashbid.offer_received':
        console.log('offer received', event.data)
        break
      default:
        console.log('unhandled', event.type)
    }
    res.sendStatus(200)
  } catch {
    // Bad signature or stale timestamp — do not trust the payload.
    res.sendStatus(400)
  }
})

The verified WebhookEvent looks like this:

{
  "id": "evt_7Qp2",
  "type": "bid.accepted",
  "created_at": "2026-06-18T15:04:00Z",
  "data": { "bid_id": "bid_7Qp2", "reference": "BID-10482", "status": "accepted" }
}

POST/v1/webhooks

Register a subscription

The signing secret is returned only once, at creation.

Request

POST
/v1/webhooks
curl https://api.mygrainexchange.com/v1/webhooks \
  -H "Authorization: Bearer {token}" \
  -H "Content-Type: application/json" \
  -d '{ "url": "https://erp.example.com/mgx/hook", "events": ["bid.accepted", "trade.settled"] }'

Response

{
  "data": {
    "id": "whk_4Tz9",
    "url": "https://erp.example.com/mgx/hook",
    "events": ["bid.accepted", "trade.settled"],
    "secret": "whsec_8fK2...shown_once"
  }
}

GET/v1/webhooks

List subscriptions

All webhook subscriptions for your team.

Request

GET
/v1/webhooks
curl https://api.mygrainexchange.com/v1/webhooks \
  -H "Authorization: Bearer {token}"

DELETE/v1/webhooks/:id

Delete a subscription

Stop delivering to a subscription.

Request

DELETE
/v1/webhooks/whk_4Tz9
curl -X DELETE https://api.mygrainexchange.com/v1/webhooks/whk_4Tz9 \
  -H "Authorization: Bearer {token}"

GET/v1/webhooks/:id/deliveries

Inspect deliveries

Recent delivery attempts (status + retry count) for debugging failed deliveries.

Request

GET
/v1/webhooks/whk_4Tz9/deliveries
curl https://api.mygrainexchange.com/v1/webhooks/whk_4Tz9/deliveries \
  -H "Authorization: Bearer {token}"

Was this page helpful?