project.payment_method.updated

A payment gateway's credentials, link state or on/off switch changes.

This payload never carries a credential. When gateway keys are rotated, changes.config.keys names which keys changed — ["clientId"], ["secret"] — and nothing else. Neither the old nor the new value is sent, and the payment_method snapshot has no config at all.

When this fires

A payment method on the project changes and at least one of active, linked or a key inside its stored config is different afterwards. The surfaces that raise it:

  • Dashboard — the payment method editor on the project's Payment Methods page (credentials and switches), and the on/off toggle on each gateway row.
  • Telegram — the main bot's payment-method wizard, when a creator walks through connecting a gateway that the project already has in that mode. The wizard re-saves the row through the same path as the dashboard.
  • RESTPOST /v1/projects/{project}/payment-methods/{method}/activate and /deactivate. There is no PATCH for payment methods, so credentials cannot be rotated over REST.
  • MCP — the activate_payment_method and deactivate_payment_method tools.

It does not fire when:

  • Nothing changed. Activating a gateway that is already on, or re-saving identical credentials, writes nothing and emits nothing.
  • A Stripe method is switched on before its Connect account is attached. The call is refused, the row is untouched and there is no event.
  • Stripe Connect onboarding completes or is disconnected. Those flows write the row directly and raise no event, even though they change linked and active.
  • A gateway is first set up. There is no project.payment_method.created event; the first thing you hear about a new gateway is its first update or its deletion.
  • The gateway is removed. That is project.payment_method.deleted.

Required ability

project-payment-method:view — token must carry this at mint time to subscribe an endpoint to this event.

Payload

{
  "id": "evt_01HX...",
  "type": "project.payment_method.updated",
  "created_at": "2026-09-06T09:20:00Z",
  "api_version": "2026-05-01",
  "project_id": "7f3d1c92-8b45-4e6a-9d21-5c8e0a4b6f13",
  "data": {
    "payment_method": {
      "id": "a15d70c8-3e46-4b92-b70f-58c9d2140e63",
      "provider": "paypal",
      "mode": "live",
      "active": true
    },
    "changes": {
      "config": {
        "keys": ["clientId"]
      }
    }
  }
}

A switch flip looks like every other changes map in the catalog:

{
  "id": "evt_01HX...",
  "type": "project.payment_method.updated",
  "created_at": "2026-09-06T09:21:00Z",
  "api_version": "2026-05-01",
  "project_id": "7f3d1c92-8b45-4e6a-9d21-5c8e0a4b6f13",
  "data": {
    "payment_method": {
      "id": "a15d70c8-3e46-4b92-b70f-58c9d2140e63",
      "provider": "paypal",
      "mode": "live",
      "active": false
    },
    "changes": {
      "active": { "from": true, "to": false }
    }
  }
}

Field reference

FieldTypeNotes
idstring ULIDUnique event id, prefixed evt_. Use for idempotent processing.
typestringAlways project.payment_method.updated for this event.
created_atISO 8601 timestampServer-side emission time.
api_versionstringWebhook API contract version.
project_idstring UUIDProject this event belongs to.
data.payment_methodobjectPost-update snapshot — id, provider, mode, active only. Never config, never linked.
data.payment_method.idstring UUIDPayment method identifier.
data.payment_method.providerstringA gateway slug (stripe, paypal, skrill, coinpayments, paystack, razorpay, ceypay, accesscode) or a connector:provider key for a currency a connector brings (telegram:stars); platformcurrency is the legacy alias earlier native rows carried.
data.payment_method.modeenum: live, testWhich of the gateway's environments this row holds. A project has at most one row per provider per mode.
data.payment_method.activebooleanWhether the gateway is offered to new buyers after the change.
data.changesobjectWhat changed. One or more of active, linked, config. Never empty — an empty map means no event.
data.changes.active.frombooleanPrevious switch position. Present only when the switch flipped.
data.changes.active.tobooleanNew switch position.
data.changes.linked.frombooleanPrevious link state. Present only when it changed.
data.changes.linked.tobooleanNew link state.
data.changes.config.keysarray of stringsThe gateway credential keys whose value changed or was newly stored, e.g. ["clientId", "secret"]. Present only when at least one did. Values are never sent.

Caveats

  • linked can appear in changes but is not in the snapshot. Read the new value from changes.linked.to, or from GET /v1/projects/{project}/payment-methods/{method}, which does carry linked.
  • mode never appears in changes. A row's mode is fixed — live and test are separate rows — so switching a gateway from test to live is a different method with a different id, not an update to this one.
  • Every update re-queues a sync of the project's plans to that gateway, so a plan.sync_completed per active plan usually follows within a minute. Rotated credentials may point at a different gateway account whose catalogue has never seen the plans.
  • config.keys are the gateway's own field names as Subscriby stores them (clientId, secret, publicKey, …), not a normalised list. Stored config is merged on save, so a key is reported when its value changed or was added — keys are never removed through an update.
  • Switching a gateway off does not affect subscriptions already sold through it; they keep renewing. It only stops new buyers being offered that gateway.

How is this guide?

On this page

Subscriby is a product designed by you — for you.

No boardroom full of executives deciding what we ships next. Our roadmap always shaped by you with your feedback.

Share feedback or a request