Events and Webhooks

You can configure webhook endpoints via APIs or Dashboard in your platform to be notified about events from Column. Each platform can create multiple webhook endpoints for different types of events. All webhook endpoints must be configured with https URLs that accept POST requests with JSON payloads.

Event Object

All Column webhook events have the same top level structure.

  • id unique identifier of an event. If a webhook event is sent multiple times, its ID will remain the same for all requests.
  • created_at time at which an event was created.
  • type description of an event (see the table below for all types of events).
  • data object containing data associated with an event. Please refer to All Event Types for the data object of each event.
{
  "id": "evnt_1vEdMiZ5pmkmrKZfNZ8LeqF2KFP",
  "created_at": "2021-07-12T23:05:36Z",
  "type": "ach.outgoing_transfer.completed",
  "data": {
    "id": "acht_1vEdMiRjQWQYqRqaOppMfU7BWr1",
    ... // rest details of ACH transfer object
  }
}

Authentication

To verify that a webhook event was actually sent by Column, every webhook event payload is signed with a signature that is passed through as the HTTP header Column-Signature. The signature is a hash-based message authentication code (HMAC) with SHA-256, by applying the webhook endpoint's signing secret as the key, and the JSON payload (i.e., the request body) as the message.

Confirmation & Retry

After an event is delivered to a webhook endpoint, and our system receives a 2XX HTTP response within 10 seconds, that event is considered having been delivered successfully. If an event delivery failed, our system will retry up to 25 times within 3 days with exponential backoffs. The first retry happens one minute after the original failed delivery. An event may be occasionally delivered multiple times to a webhook endpoint with the same unique event ID. We advise you to guard against duplicated event receipts by making your event processing idempotent. In addition, we would suggest you to return a 2XX HTTP response right after an event is received, before you actually process it in your system, to avoid any potential timeout error.

Event Ordering

Our webhook system does NOT guarantee that events will be delivered in the same order as they are created. For example, the following events might be created for an ACH transfer:

  • ach.outgoing_transfer.initiated
  • ach.outgoing_transfer.submitted
  • ach.outgoing_transfer.settled
  • ach.outgoing_transfer.acknowledged

An endpoint should not expect delivery of those events in this order and should handle it accordingly. In most cases, out of order event delivery happens when a previous delivery failed. For example, if our post to your webhook endpoint timed out when we tried to deliver ach.outgoing_transfer.initiated, it will be rescheduled with an exponential backoff time. If ach.outgoing_transfer.submitted is scheduled before that, and we successfully delivered it to your endpoint, ach.outgoing_transfer.initiated will be delivered after ach.outgoing_transfer.submitted. You can use our event APIs to fetch any missing or additional events, if necessary.

Examples

Setup a webhook endpoint

Each webhook endpoint must be configured to accept certain types of events by specifying them in the enabled_events list. You can use one webhook endpoint to handle several different event types at once or set up individual endpoints for specific types of events.

Wildcard * is supported as well. For example, you can use ach.* to accept all ACH transfer events, or * for all webhook events. Please be careful when using *, as it may overload your webhook servers. If multiple endpoints are configured for a certain event type, events of that type will be sent to the first configured endpoint.

curl https://api.column.com/api/webhook_endpoints \
  -X POST \
  -u :<api_key> \
  -d url="https://example.com/webhook" \
  -d description="endpoint for ACH events" \
  -d "enabled_events[]"="ach.*" \
  -d "enabled_events[]"="book.outgoing_transfer.completed"

Check the webhook signature

Column signs the webhook events sent to your endpoints by including a signature in each event's Column-Signature header. This allows you to verify that the events were sent by Column, not by a third party. Before you can verify signatures, you need to retrieve your endpoint's secret from your Dashboard's Webhook settings. Column generates a unique secret key for each endpoint.

Once you have the endpoint secret and the raw request body content of a webhook, you can compute an HMAC with the SHA256 hash function, by using the secret as the key and request body as the message. The result should be equal to the value of Column-Signature header for a valid webhook event sent by Column.

Fetch events

You can fetch all events (including events that are not sent via webhooks) that happened via our /api/events API. If you just want to fetch events that were sent to your webhook endpoints, you can use our /api/events/webhook API. Please refer to All Event Types for more information.

Fetch event delivery history

You can fetch the history of webhook deliveries for a certain endpoint or event. The responses of such requests will contain list of all webhook delivery attempts and their statuses based on query filters.

{
  "webhook_deliveries":  [
    {
      "event":  {
        ... // event object defined above
      },
      "scheduled_at":  "2021-07-19T17:34:34Z",
      "status":  "SUCCEEDED"
    },
    ... // more webhook delivery attempt
  ]
}

Protect endpoint secrets

Endpoint signing secrets are critical for you to authenticate webhook events from Column. If an endpoint's secret is believed to be compromised, you can create a new webhook subscription endpoint with the same URL and enabled events. Column will continue sending webhook events to the old endpoint until you have updated your system with the new endpoint secret and deleted the old endpoint. At that point, Column will start to send webhook events to the new endpoint.

All Event Types

This is a list of all the types of events we currently create. Note that this list is a work-in-progress. We may add or delete events at any time, so please keep that in mind while developing and maintaining your code. All event types follow a pattern {category}.{action} or {category}.{sub_category}.{action}.

Not all events will be sent via webhook endpoints. Events marked as WEBHOOK: FALSE are only created but not sent via webhook. However, you can fetch all types of events via our event APIs.

ACH Transfer Events


For all ACH transfer events, data in the webhook response will be the ACH Transfer Object at the time the webhook is fired.

ach.outgoing_transfer.initiated Column has received your ACH transfer request and will submit it to the Federal Reserve Bank at the next settlement window.

ach.outgoing_transfer.submitted Column has submitted the ACH transfer request to the Federal Reserve Bank.

ach.outgoing_transfer.acknowledged Column has received an acknowledgement from the RDFI for a CCD or CTX ACH transfer initiated by your platform. Note: some RDFIs may not send acknowledgements even if we request them to send.

ach.outgoing_transfer.settled The ACH transfer has been settled at the RDFI. For outgoing ACH debit transfers, funds will be available to withdraw once they are settled.

ach.outgoing_transfer.completed The return window has passed for this ACH transfer, and it is deemed officially completed. In a few very rare cases, it can still be returned by the RDFI with return code R06 or R31 (Return Reason Codes).

ach.outgoing_transfer.returned The outgoing ACH transfer has been returned by the RDFI. Return details are available in ach_transfer.return_details.

ach.outgoing_transfer.return_dishonored The return from the RDFI of an outgoing ACH transfer has been dishonored by Column. Return details are available in ach_transfer.return_details.

ach.outgoing_transfer.return_contested The dishonored return from Column of an outgoing ACH transfer has been contested by the RDFI. Return details are available in ach_transfer.return_details.

ach.outgoing_transfer.canceled The ACH transfer has been canceled before Column submits it to the Federal Reserve Bank per your request.

ach.incoming_transfer.scheduled Column has received an incoming ACH transfer (either credit or debit) from another ODFI, and scheduled to process it on its settlement date.

ach.incoming_transfer.settled Column has received an incoming ACH transfer (either credit or debit) from another ODFI, and settled it successfully.

ach.incoming_transfer.completed The return window has passed for this incoming ACH transfer, and it is deemed officially completed. In a few very rare cases, you can still request to return it with return code R06 or R31 (Return Reason Codes).

ach.incoming_transfer.returned The incoming ACH transfer has been returned by Column. Return details are available in ach_transfer.return_details.

ach.incoming_transfer.return_dishonored The return sent by Column for an incoming ACH transfer has been dishonored by the ODFI. Return details are available in ach_transfer.return_details.

ach.incoming_transfer.return_contested The dishonored return from the ODFI of an incoming ACH transfer has been contested by Column. Return details are available in ach_transfer.return_details.

ach.incoming_transfer.nsf Column has received an incoming ACH debit transfer request as the RDFI. However, there are insufficient funds to cover the ACH debit in the customer's account at Column. You have a specified amount of time to take actions (e.g. notify customer to fund this account) so the request won't be returned (the transfer is in pending_return status). If nothing changes, the request will be returned to the ODFI with R01 Return Code. Please refer here for more details.

Book Transfer Events


For all Book transfer events, data in the webhook response will be the Book Transfer Object at the time the webhook is fired.

book.transfer.initiated WEBHOOK: FALSE Column has received your book transfer request.

book.transfer.completed Column has processed your book transfer request successfully. This event may be fired directly upon creating a book transfer, or in the case of a hold, when the clear endpoint is called.

book.transfer.hold_created Column has received your book transfer request with a hold.

book.transfer.updated Column has updated your book transfer in a hold state successfully.

book.transfer.canceled Column has canceled your book transfer in a hold state successfully. Funds that were in a hold state because of this transfer are now available.

Wire Transfer Events


For all Wire transfer events, data in the webhook response will be the Wire Transfer Object at the time the webhook is fired.

wire.outgoing_transfer.initiated Column has received your wire transfer request and will submit it to the Federal Reserve Bank soon.

wire.outgoing_transfer.submitted Column has submitted the wire transfer request to the Federal Reserve Bank.

wire.outgoing_transfer.rejected The wire transfer request has been rejected by Column or by the Federal Reserve (this event will rarely happen).

wire.outgoing_transfer.completed Column has received an acknowledgement from the Federal Reserve that this wire transfer request has been processed successfully.

wire.incoming_transfer.completed Column has received an incoming wire transfer (credit only) from another ODFI, and processed it successfully.

wire.incoming_transfer.rejected Column has received an incoming wire drawdown request from another ODFI, and rejected it.

Identity Verification Events


For all Identity verification events, data in the webhook response will be the Entity Object at the time the webhook is fired.

identity.verification.pending Column has submitted the identity information for verification, and is awaiting the result.

identity.verification.manual_review The identity information is being manually reviewed.

identity.verification.verified The identity information has been verified successfully.

identity.verification.denied Column has failed to verify the identity information and denied the service request.

Loan Events


For all Loan events, data in the webhook response will be the Loan Object at the time the webhook is fired.

loan.created A loan has been created in Column's system.

loan.updated A loan has been updated in Column's system.

loan.in_dispute A loan has been updated to in_dispute in Column's system.

loan.delinquent A loan has been updated to delinquent in Column's system.

loan.paid_off A loan has been updated to paid_off in Column's system.

loan.charged_off A loan has been updated to charged_off in Column's system.

loan.canceled A loan has been canceled in Column's system.

loan.disbursement.hold_created A loan disbursement with a hold has been created in Column's system.

loan.disbursement.hold_updated A loan disbursement with a hold has been updated in Column's system.

loan.disbursement.hold_canceled A loan disbursement with a hold has been canceled in Column's system.

loan.disbursement.completed A loan disbursement been successfully completed in Column's system.

loan.payment.completed A loan payment has been successfully completed in Column's system.