Subscribing to Webhooks Using OAuth with JWT

The OAuth with JWT flow is an authentication method that is similar to the OAuth 2.0 with Client Credentials flow, except in OAuth with JWT the client will send a JSON Web Token. With this option you can bypass domain headers and minimize the need for server-side authentication checks.
This section describes the tasks that must be accomplished in order to subscribe to webhooks using OAuth with JSON Web Token (JWT).

Webhooks Subscription Workflow Using OAuth with JWT

Follow these steps to set up your webhooks subscription using OAuth with JWT:
  1. Set up your server security to receive webhooks notifications. Include app-specific certifications.
  2. Generate API security keys for authenticating API requests. You must generate separate keys for the testing and production environments.
  3. Request a product list.
  4. Subscribe to webhooks event notifications.

Setting Up Server Security

Take these actions to secure your server to receive webhooks notifications.

Allowlist

Allow these IP addresses. Our test and production servers can use either address:
  • 198.241.206.21
  • 198.241.207.21

Trusting the Root Certificate

Download the certificate, "Visa Corporate Root CA G2" from enroll.visaca.com, and add it to your Java keystore.

Create a P12 File

Follow these steps to create a .p12 file if you are using JSON Web Tokens to secure communication.
  1. Log in to the
    Business Center
    :
  2. On the left navigation panel, navigate to
    Payment Configuration > Key Management
    .
  3. Click
    + Generate key
    .
  4. Under REST APIs, select
    REST – Certificate
    and then click
    Generate key
    .
  5. Click
    Download key
    .
  6. Create a password for the certificate by entering the password into the New Password and Confirm Password fields, and then click
    Generate key
    .
    The .p12 file is downloaded to your desktop.
When you generate one or more keys, you can view the keys on the Key Management page.

Requesting a Digital Signature Key

A digital signature key is required before you can subscribe to webhooks notifications. The API request to generate the digital signature key is documented in this section. We use the digital signature key to add a unique signature to each notification. You can use the digital signature key to validate the integrity of webhooks notifications and prevent replay attacks. To verify the integrity of a notification payload using the digital signature key, see Notification Validation.
To request the service, use the endpoint specified below. After you send the request, check the response message to verify that the request was successful. A 200-level response code indicates success.
For information about response codes, see
Transaction Response Codes
.

Endpoint

  • Test:
    POST
    apitest.cybersource.com
    /kms/egress/v2/keys-sym
  • Production:
    POST
    api.cybersource.com
    /kms/egress/v2/keys-sym
  • Production in India
    :
    POST api.in.cybersource.com/kms/egress/v2/keys-sym

Required Fields for Requesting a Digital Signature Key

clientRequestAction
Set the value to
CREATE
.
keyInformation.expiryDuration
keyInformation.keyType
Set the value to
sharedSecret
.
keyInformation.organizationId
Set the value to the organization ID of the organization requesting the key.
keyInformation.provider
Set the value to
nrtd
.
keyInformation.tenant
Set the value to the organization ID of the organization requesting the key.

Example: Requesting a Digital Signature Key

{ "clientRequestAction": "CREATE", "keyInformation": { "provider": "nrtd", "tenant": "merchantName", "keyType": "sharedSecret", "organizationId": "merchantName" } }
{ "submitTimeUtc": "2021-03-17T06:53:06+0000", "status": "SUCCESS", "keyInformation": { "provider": "NRTD", "tenant": "merchantName", "organizationId": "merchantName", "keyId": "bdc0fe52-091e-b0d6-e053-34b8d30a0504", //ID associated with the key in the key field "key": "u3qgvoaJ73rLJdPLTU3moxrXyNZA4eo5dklKtIXhsAE=", //Base64 encoded key "keyType": "sharedSecret", "status": "Active", "expirationDate": "2022-03-17T06:53:06+0000" }

Sending OAuth Credentials

If you use OAuth, you must send credentials for your OAuth server so that notifications are authenticated.
To request the service, use the endpoint specified below. After you send the request, check the response message to verify that the request was successful. A 200-level response code indicates success.
For information about response codes, see
Transaction Response Codes
.

Endpoint

  • Test:
    POST
    apitest.cybersource.com
    /kms/egress/v2/keys-sym
  • Production:
    POST
    api.cybersource.com
    /kms/egress/v2/keys-sym
  • Production
    in India:
    POST api.in.cybersource.com/kms/egress/v2/keys-sym

Required Fields for Sending Your OAuth Credentials

clientRequestAction
Set the value to
STORE
.
keyInformation.clientKeyId
Set the value to the OAuth client's username.
keyInformation.expiryDuration
Set the value to
365
.
keyInformation.key
Set the value to the client's secret key.
keyInformation.keyType
Set the value to
oAuthClientCredentials
.
keyInformation.organizationId
Set the value to the organization ID of the organization requesting the key.
keyInformation.provider
Set to the value of the
organizationId
field that is assigned to the organization sending the request.
keyInformation.tenant
Set the value to
nrtd
.

Example: Sending Your OAuth Credentials

Store oAuth Credentials
{ "clientRequestAction": "STORE", "keyInformation": { "provider": "merchantName", "tenant": "nrtd", "keyType": "oAuthClientCredentials", "organizationId": "merchantName", "clientKeyId": "client username", "key": "client secret", "expiryDuration": "365" } }
{ "submitTimeUtc": "2022-02-18T19:49:52Z", "status": "SUCCESS", "keyInformation": { "provider": "org1", "tenant": "nrtd", "organizationId": "org1", "clientKeyId": "ef400ac1-edfe-406e-94b3-0d73be09a1a0", "keyId": "d8512fb5-1d8c-4f2d-e053-3cb8d30a764c", "key": "KTTY1LLGYR6A2LL4XZTT9W9RGCVJ5Z4XZAP6AFTRUFWLSXX0NX4N88N9EJED3BMM", "keyType": "oAuthClientCredentials", "status": "active", "expirationDate": "2023-02-18T19:49:52Z" } }

Products and Events

Discover the products and events to which you can subscribe by sending an API request to retrieve the list of all of the products and events that are enabled and configured for your account. You can subscribe to webhooks only for products and services that are enabled and configured for your account. The API response includes an array of products with all of the event types included in the
eventTypes
fields.

Requesting a List of Products and Events

To subscribe to a webhook, your account must be enabled for the product associated with the webhook. To get a list of products and event types that are enabled for your organization, send a GET request. For more information, see Product and Event Types.

Endpoint

  • Test:
    GET
    apitest.cybersource.com
    /notification-subscriptions/v1/products/{organizationId}
  • Production:
    GET
    api.cybersource.com
    /webhooks/notification-subscriptions/v1/products/{organizationId}
  • Production
    in India:
    GET api.in.cybersource.com/notification-subscriptions/v1/products/{organizationId}

Example: Requesting a List Products and Event

GET
https://api.cybersource.com
/notification-subscriptions/v1/products/{organizationId}
[ { "productId": "terminalManagement", "eventTypes": [ { "eventName": "terminalManagement.status.update", "payloadEncryption": false }, { "eventName": "terminalManagement.assignment.update", "payloadEncryption": false }, { "eventName": "terminalManagement.reAssignment.update", "payloadEncryption": false } ] } ]

Subscribe to Webhooks Using OAuth with JWT

This section describes the tasks that must be accomplished in order to subscribe to a webhook using OAuth with a JSON Web Token (JWT). You can only subscribe to one webhook per API request.
To request the service, use the endpoint specified below. After you send the request, check the response message to verify that the request was successful. A 200-level response code indicates success.
For information about response codes, see
Transaction Response Codes
.

Endpoint

  • Test:
    POST
    apitest.cybersource.com
    /notification-subscriptions/v1/webhooks
  • Production:
    POST
    api.cybersource.com
    /notification-subscriptions/v1/webhooks
  • Production in India:
    POST api.in.cybersource.com/notification-subscriptions/v1/webhooks

Required Fields for Subscribing to Webhooks Using OAuth with JWT

eventTypes
healthCheckUrl
Required in order to auto-activate the subscription. If you do not send this field, the subscription will be created but not activated. To activate it later, you must send a PATCH request that includes a health check URL. An inactive subscription will not send notifications. For more information, see Health Check URL.
organizationId
productId
For a list of product and event types, see Product and Event Types.
securityPolicy.securityType
Set the value to
oAuth_JWT
.
securityPolicy.config.oAuthTokenExpiry
Set the value to
365
.
securityPolicy.config.oAuthUrl
securityPolicy.config.oAuthTokenType
Set the value to
Bearer
.
securityPolicy.config.additionalConfig.aud
securityPolicy.config.additionalConfig.client_id
securityPolicy.config.additionalConfig.keyId
securityPolicy.config.additionalConfig.scope
webhookUrl

Optional Fields for Subscribing to Webhooks Using OAuth with JWT

description
healthCheckUrl
This field is not required in order to create a subscription, but it is required in order to activate it. If you do not send this field during subscription, the subscription is created but not activated. To activate it later, you must send a PATCH request that includes a health check URL. An inactive subscription does not send notifications. For more information, see Health Check URL.
name
notificationScope.scopeData
Required if you submit the
notificationScope.scope
field. All organizations subscribing to the webhook must be listed in the value, separated by commas.
notificationScope.scope
For more information, see Notification Scope.
retryPolicy.deactivateFlag
retryPolicy.firstRetry
retryPolicy.interval
retryPolicy.numberOfRetries
retryPolicy.repeatSequenceCount
retryPolicy.repeatSequenceWaitTime

Example: Creating a Webhook Subscription Using OAuth with JWT

{ "name": "My Custom Webhook", "description": "Sample Webhook from Developer Center", "organizationId": "organizationId", "productId": "terminalManagement", "eventTypes": [ "terminalManagement.assignment.update" ], "webhookUrl": "https://MyWebhookServer.com:443/simulateClient", "healthCheckUrl": "https://MyWebhookServer.com:443/simulateClientHealthCheck", "notificationScope": "SELF", "securityPolicy": { "securityType": "oAuth_JWT", "proxyType": "external", "config": { "oAuthTokenExpiry": 365, "oAuthURL": "https://MyWebhookServer.com:443/oAuthToken", "oAuthTokenType": "Bearer", "additionalConfig": { "aud": "idp.api.myServer.com", "client_id": "650538A1-0000-0000-0000-932ABC57AD70", "keyId": "y-00000000000000-eAZ34pR9Ts", "scope": "merchantacq:rte:write" } } } }
{ "organizationId": "organizationId", "productId": "terminalManagement", "eventTypes": [ "terminalManagement.assignment.update" ], "webhookId": "fe46bf08-3918-21ba-e053-a1588d0aeefa", "name": "My Custom Webhook", "webhookUrl": "https://MyWebhookServer.com:443/simulateClient", "healthCheckUrl": "https://MyWebhookServer.com:443/simulateClientHealthCheck", "createdOn": "2023-06-16T21:19:54.667Z", "status": "INACTIVE", "description": "Sample Webhook from Developer Center", "retryPolicy": { "algorithm": "ARITHMETIC", "firstRetry": 1, "interval": 1, "numberOfRetries": 3, "deactivateFlag": false, "repeatSequenceCount": 0, "repeatSequenceWaitTime": 0 }, "securityPolicy": { "securityType": "oAuth_JWT", "proxyType": "external", "digitalSignatureEnabled": "yes", "config": { "oAuthTokenExpiry": 365, "oAuthURL": "https://MyWebhookServer.com:443/oAuthToken", "oAuthTokenType": "Bearer", "additionalConfig": { "aud": "idp.api.myServer.com", "client_id": "650538A1-0000-0000-0000-932ABC57AD70", "keyId": "y-00000000000000-eAZ34pR9Ts", "scope": "merchantacq:rte:write" } } }, "version": "3", "deliveryType": "nrtdCentral", "notificationScope": "SELF" }

Notification Scope

The value of the
notificationScope.scope
field determines which organizations receive the notification. This field can take any of these values:
SELF
Subscribes to event notifications for only the organization requesting the webhook subscription.
DESCENDENTS
Subscribes to event notifications for all child organizations of the requesting organization.
CUSTOM
Subscribes to event notifications for organizations listed in the
notificationScope.scopeData
array. Organizations must be separated by commas.

Health Check URL

When you subscribe to a webhook, it is recommended that you include a health check URL. While it is not required to create a subscription, the subscription is not activated until a health check URL is added to the subscription.
If
deactivateflag=true
was set in the subscription request and your webhook URL or health check URL become unresponsive, any new notifications will be held and their respective subscriptions will have a status of
SUSPENDED
. When the URLs becomes responsive, your subscriptions will return to a status of
ACTIVE
and notifications will resume.
Providing a health check URL for each webhook implements two features: suspend and resume and automatic activation.

Suspend and Resume

The webhook delivery framework can detect when a webhook URL is unavailable and suspend the delivery of notifications. When your webhook URL is available, the webhook framework resumes delivery of webhook notifications, ensuring that there are no missed notifications. To implement this feature, you must include the
healthCheckurl
field when you send the API request that creates the webhook subscription. For more information, see Set Up Webhooks.

Automatic Activation

When you include the
healthCheckUrl
field in the API request that creates a webhook subscription, the webhook is automatically activated. If you do not add a health check URL when you create the subscription, the subscription is not activated. To activate it later, send a PATCH request that includes a health check URL. An inactive subscription does not send notifications.

Subscription Status

A subscription can have one of three statuses:
ACTIVE
The subscription is ready to send notifications or is actively sending notifications.
INACTIVE
The subscription has not been activated. Add a health check URL to activate. See Health Check URL.
SUSPENDED
The subscription was active, but the webhook URL or the health check URL became unreachable. When the URL becomes reachable, the status changes to
ACTIVE
and notifications resume.

Product and Event Types

When you send an API request to create a webhooks subscription, you must include the product and its associated events to which you are subscribing. You can include only one product per API request, so you must send separate requests for each product and its associated events. Each time one of the listed events occurs, you receive a notification. If a product is not yet available, you receive the notifications when it becomes available. To obtain a list of products to which you can subscribe, see Requesting a List of Products and Events.
To create a webhooks subscription, set the
productId
field to a value listed in the product ID column, and set the
eventTypes
field array to a value or values listed in the event types column. These are the supported products and events and their respective
productId
and
eventTypes
field values:
Alternative Payments
Product ID
Event Types
Description
alternativePaymentMethods
payments.payments.updated
Notifies you that an alternative payment transaction's status has been updated.
Fraud Management Essentials
Product ID
Event Types
Description
fraudManagementEssentials
risk.casemanagement.decision.accept
Notifies you that a
Fraud Management Essentials
case has been accepted.
risk.casemanagement.addnote
Notifies you that a note has been added to a
Fraud Management Essentials
case.
risk.profile.decision.reject
Notifies you that a transaction was rejected.
risk.casemanagement.decision.reject
Notifies you that a
Fraud Management Essentials
case has been rejected.
risk.profile.decision.monitor
Notifies you of a profile decision to monitor a transaction.
risk.profile.decision.review
Notifies you of a profile decision to review a transaction.
Invoicing
Product ID
Event Types
Description
customerInvoicing
invoicing.customer.invoice.send
Notifies you that an invoice has been sent.
invoicing.customer.invoice.cancel
Notifies you that an invoice has been cancelled.
invoicing.customer.invoice.paid
Notifies you that an invoice has been paid.
invoicing.customer.invoice.partial-payment
Notifies you that an invoice has been partially paid.
invoicing.customer.invoice.reminder
Notifies you 5 days before the invoice payment is due.
This event is triggered if you have invoice reminders enabled in your invoice settings.
invoicing.customer.invoice.overdue-reminder
Notifies you 1 day after the invoice payment is due.
This event is triggered if you have invoice reminders enabled in your invoice settings.
Outage Alerts
Product ID
Event Types
Description
cns
cns.report.keyExpiration.detail
Notifies you that a key is expiring.
Recurring Billing
Product ID
Event Types
Description
recurringBilling
rbs.subscriptions.charge.failed
Notifies you of a recurring payment failure.
rbs.subscriptions.charge.pre-notified
Notifies you of an upcoming recurring payment.
rbs.subscriptions.charge.created
Notifies you of successful recurring payment.
Token Management Service
Product ID
Event Types
Description
tokenManagement
tms.networktoken.updated
Notifies you of a network token's change in expiration date or status (suspend, resume, or deactivate).
tms.networktoken.provisioned
Notifies you when a network token provision for an instrument identifier token has been successful.
Example: Product and Events in a Webhook Subscription
"productId": "tokenManagement", "eventTypes": [ "tms.networktoken.provisioned", "tms.networktoken.updated", "tms.token.pan_updated", "tms.token.created", "tms.token.updated" ]

Related Information

Set Up Webhooks

Notification Format

Each event notification contains headers and the body of the message.

Notification Headers

These headers are sent with every notification. Some headers are duplicates of the fields in the body.
V-C-signature
Contains the digital signature, which can be used for validating the security of the notification.
V-C-event-type
Type of event that generated the notification.
V-C-organization-id
Identifier of the organization that subscribed to the notification.
V-C-product-name
Name of the product for which the event occurred.
V-C-request-type
New or retry.
V-C-retry-count
Number of times the notification was resent.
V-C-transaction-trace-id
Identifier of the notification attempt. For example, every time a notification is retried, each attempt has a different transaction trace ID and the same notification ID.
v-c-webhook-id
Identifier of the webhook subscription that generated the notification.

Notification Body

The body of the message contains fields associated with the notification itself and the payload of the event that generated the notification.
webhookId
Identifier of the webhook subscription that generated the notification.
transactionTraceId
Identifier of the notification attempt. For example, every time a notification is retried, each attempt has a different transaction trace ID and the same webhook ID.
productId
Identifier of the product that generated the event.
organizationId
Identifier of the organization that subscribed to the notification.
eventType
The type of event that generated the notification.
eventDate
Timestamp of the event.
payload
The data generated by the event.
requestType
New or retry.

Example: Notification Payload

Invoicing Webhook Notification
POST /test HTTP/1.1 Host: invoicetest.example.com Content-Length: 1647 Content-Type: application/json User-Agent: Vert.x-WebClient/3.9.8 V-C-Event-Type: invoicing.customer.invoice.send V-C-Organization-Id: invoicetest V-C-Product-Name: customerInvoicing V-C-Request-Type: NEW V-C-Retry-Count: 0 V-C-Signature: t=168506218354;keyId=facdaf45-db00-233c-e053-5a588d0a743a;sig=kJgSHwHrRgUmXJdaSSqtPVzOTMlV3SW90WAFIfPj4XA= V-C-Transaction-Trace-Id: 8c01a8e9b3334d19528d9b69a21fe797ebaf8dd3dee52f422dd699af26bd866-0 V-C-Webhook-Id: fc8a9385-723e8e28-e053-a2588e0a536d { "notificationId":"fc8f1cae-1232-5dd-e053-a0588e0a5eeb", "retryNumber":0, "eventType":"invoicing.customer.invoice.send", "eventDate":"2023-05-25T17:49:40.309-07:00", "webhookId":"fc8a9385-723e-8e28-e053-a2588e0a536d", "payloads":[ { "data":{ "merchantZip":"12345", "thankYou":"Thank you for your business!", "subject":"You've received an invoice 202203789 from Company, Inc.", "dueDate":"2023-05-25", "link":"Click the link below to view and pay the invoice.", "body":"You have a new invoice from Company, Inc. for 102.00 ALL due on 2023-05-25.", "merchantState":"OH", "merchantName":"Company, Inc.", "button":"VIEW AND PAY INVOICE", "invoiceBalance":"102.00", "merchantAddress2":" ", "invoiceNumber":"20220389", "payerName":"Firstname Lastname", "customMessage":"Thank you for your business. By clicking the view and pay invoice you agree to the terms and conditions here: https://paymenttermsandconditions.example.com", "currency":"ALL", "merchantCity":"Placeville", "emailTo":"Person@example.com", "eventType":"invoicing.customer.invoice.send", "balanceAmount":"102.00", "organizationID":"invoicetest", "merchantPhone":"321-321-3211", "emailLanguage":"en-us", "invoiceUrl":"
https://businesscentertest.cybersource.com
/ebc2/invoicing/payInvoice/u2wJqUKII4FsqtIrZx51lUuvYr5Msz23nqIx12xqJhs7wqT6Th2mJcDOYHSC5hE?version=v2.1", "merchantAddress":"39 E. Road St", "correlationID":"dceb3ce-d787-453c-84a3-ce33b141cc76", "hello":"Hello Person," }, "preferences":{ "contacts":[ { "firstName":"Invoicing", "lastName":"Email", "contact":"person@example.com", "type":"EMAIL" } ] }, "organizationId":"invoicetest", "metadata":{ "from.displayName":"Company, Inc.", "reply.email":"noreply@example.com", "sentBy":"Invoice" } } ] }