CorrConnectDocs
Early accessRequest access

API reference

Webhook subscription endpoints

Manage where events go. The events themselves are in the webhook reference; the receiving side is in receive webhooks.

Create a webhook subscription

POST/v1/developer/apps/{app_id}/webhooks

The response carries secret (whsec_...) this once. Store it; CorrConnect never shows it again. events is optional and defaults to activity.created and grant.revoked.

Auth
Console session cookie, Origin and X-CSRF-Token
Host
Portal origin only

Parameters

NameInTypeNotes
app_id requiredpathstring
Origin requiredheaderstringExactly the CorrConnect portal origin.
X-CSRF-Token requiredheaderstringThe latest csrf_token from sign-in or GET /v1/account/me.

Request body application/json

FieldTypeNotes
callback_url requiredstringPublic HTTPS URL on port 443 (10 to 2,000 characters). 10 to 2000 characters
eventsarray of string or nullOptional. activity.created, grant.revoked or both (the default).

Response 201

Successful Response

FieldTypeNotes
id requiredstring
callback_url requiredstring
events requiredarray of string
status requiredstringOne of active, disabled
consecutive_failures requiredinteger
created_at requiredstring (date-time)
updated_at requiredstring (date-time)
secret requiredstring

Errors

StatusCodeWhen
400invalid_callback_urlThe URL is not public HTTPS on port 443, or has credentials, a fragment or a wildcard.
400invalid_event_typeevents lists something other than activity.created or grant.revoked.
401authentication_requiredNo console session cookie was sent.
401invalid_sessionThe session was signed out, replaced or never existed.
401session_expiredThe session is older than its lifetime (12 hours by default).
403email_verification_requiredThe account has not verified its email address yet.
403origin_rejectedThe Origin header is not the exact CorrConnect portal origin.
403csrf_rejectedX-CSRF-Token is missing or is not the latest token from GET /v1/account/me.
404app_not_foundThe app does not exist or belongs to another account.
409webhook_limit_reachedThe app already has 3 subscriptions.
422validation_failedA field is missing, has the wrong type or is out of range; details.fields says which.
429rate_limit_exceededMore than 20 subscriptions created by this account in an hour.
cURL
curl "$CORRCONNECT_BASE/v1/developer/apps/$APP_ID/webhooks" \
  -H "Cookie: $CORRCONNECT_COOKIE" \
  -H "Origin: $CORRCONNECT_BASE" \
  -H "X-CSRF-Token: $CSRF_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"callback_url": "https://developer.example/hooks/corrconnect", "events": ["activity.created", "grant.revoked"]}'
JavaScript
const response = await fetch(`${process.env.CORRCONNECT_BASE}/v1/developer/apps/${process.env.APP_ID}/webhooks`, {
  method: "POST",
  headers: {
    "Cookie": `${process.env.CORRCONNECT_COOKIE}`,
    "Origin": `${process.env.CORRCONNECT_BASE}`,
    "X-CSRF-Token": `${process.env.CSRF_TOKEN}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({"callback_url": "https://developer.example/hooks/corrconnect", "events": ["activity.created", "grant.revoked"]}),
});
console.log(response.status, await response.json());
Python
import os

import requests

response = requests.post(
    f"{os.environ['CORRCONNECT_BASE']}/v1/developer/apps/{os.environ['APP_ID']}/webhooks",
    headers={"Cookie": f"{os.environ['CORRCONNECT_COOKIE']}", "Origin": f"{os.environ['CORRCONNECT_BASE']}", "X-CSRF-Token": f"{os.environ['CSRF_TOKEN']}"},
    json={"callback_url": "https://developer.example/hooks/corrconnect", "events": ["activity.created", "grant.revoked"]},
    timeout=10,
)
print(response.status_code, response.json())
Swift
import Foundation

let env = ProcessInfo.processInfo.environment
var request = URLRequest(url: URL(string: "\(env["CORRCONNECT_BASE"]!)/v1/developer/apps/\(env["APP_ID"]!)/webhooks")!)
request.httpMethod = "POST"
request.setValue("\(env["CORRCONNECT_COOKIE"]!)", forHTTPHeaderField: "Cookie")
request.setValue("\(env["CORRCONNECT_BASE"]!)", forHTTPHeaderField: "Origin")
request.setValue("\(env["CSRF_TOKEN"]!)", forHTTPHeaderField: "X-CSRF-Token")
request.setValue("application/json", forHTTPHeaderField: "Content-Type")
request.httpBody = Data(#"{"callback_url": "https://developer.example/hooks/corrconnect", "events": ["activity.created", "grant.revoked"]}"#.utf8)
let (data, response) = try await URLSession.shared.data(for: request)
print((response as! HTTPURLResponse).statusCode, String(decoding: data, as: UTF8.self))
Response 201
{
  "id": "whs_0123456789abcdef0123456789abcdef",
  "callback_url": "https://developer.example/hooks/corrconnect",
  "events": [
    "activity.created",
    "grant.revoked"
  ],
  "status": "active",
  "consecutive_failures": 0,
  "created_at": "2026-10-09T12:00:00+00:00",
  "updated_at": "2026-10-09T12:00:00+00:00",
  "secret": "whsec_<shown-once>"
}

List webhook subscriptions

GET/v1/developer/apps/{app_id}/webhooks

Oldest first. Secrets are never returned.

Auth
Console session cookie
Host
Portal origin only

Parameters

NameInTypeNotes
app_id requiredpathstring

Response 200

Successful Response

FieldTypeNotes
data requiredarray of object
data[].id requiredstring
data[].callback_url requiredstring
data[].events requiredarray of string
data[].status requiredstringOne of active, disabled
data[].consecutive_failures requiredinteger
data[].created_at requiredstring (date-time)
data[].updated_at requiredstring (date-time)

Errors

StatusCodeWhen
401authentication_requiredNo console session cookie was sent.
401invalid_sessionThe session was signed out, replaced or never existed.
401session_expiredThe session is older than its lifetime (12 hours by default).
403email_verification_requiredThe account has not verified its email address yet.
404app_not_foundThe app does not exist or belongs to another account.
cURL
curl "$CORRCONNECT_BASE/v1/developer/apps/$APP_ID/webhooks" \
  -H "Cookie: $CORRCONNECT_COOKIE"
JavaScript
const response = await fetch(`${process.env.CORRCONNECT_BASE}/v1/developer/apps/${process.env.APP_ID}/webhooks`, {
  headers: {
    "Cookie": `${process.env.CORRCONNECT_COOKIE}`,
  },
});
console.log(response.status, await response.json());
Python
import os

import requests

response = requests.get(
    f"{os.environ['CORRCONNECT_BASE']}/v1/developer/apps/{os.environ['APP_ID']}/webhooks",
    headers={"Cookie": f"{os.environ['CORRCONNECT_COOKIE']}"},
    timeout=10,
)
print(response.status_code, response.json())
Swift
import Foundation

let env = ProcessInfo.processInfo.environment
var request = URLRequest(url: URL(string: "\(env["CORRCONNECT_BASE"]!)/v1/developer/apps/\(env["APP_ID"]!)/webhooks")!)
request.setValue("\(env["CORRCONNECT_COOKIE"]!)", forHTTPHeaderField: "Cookie")
let (data, response) = try await URLSession.shared.data(for: request)
print((response as! HTTPURLResponse).statusCode, String(decoding: data, as: UTF8.self))
Response 200
{
  "data": [
    {
      "id": "whs_0123456789abcdef0123456789abcdef",
      "callback_url": "https://developer.example/hooks/corrconnect",
      "events": [
        "activity.created",
        "grant.revoked"
      ],
      "status": "active",
      "consecutive_failures": 0,
      "created_at": "2026-10-09T12:00:00+00:00",
      "updated_at": "2026-10-09T12:00:00+00:00"
    }
  ]
}

Send a test event

POST/v1/developer/apps/{app_id}/webhooks/{subscription_id}/test

Queues one webhook.test event to this subscription, signed like any other event.

Auth
Console session cookie, Origin and X-CSRF-Token
Host
Portal origin only

Parameters

NameInTypeNotes
app_id requiredpathstring
subscription_id requiredpathstring
Origin requiredheaderstringExactly the CorrConnect portal origin.
X-CSRF-Token requiredheaderstringThe latest csrf_token from sign-in or GET /v1/account/me.

Response 202

Successful Response

FieldTypeNotes
status requiredstringOne of queued
delivery_id requiredstring

Errors

StatusCodeWhen
401authentication_requiredNo console session cookie was sent.
401invalid_sessionThe session was signed out, replaced or never existed.
401session_expiredThe session is older than its lifetime (12 hours by default).
403email_verification_requiredThe account has not verified its email address yet.
403origin_rejectedThe Origin header is not the exact CorrConnect portal origin.
403csrf_rejectedX-CSRF-Token is missing or is not the latest token from GET /v1/account/me.
404app_not_foundThe app does not exist or belongs to another account.
404webhook_not_foundThe app has no subscription with this ID.
409webhook_disabledThe subscription is paused; enable it first.
429rate_limit_exceededMore than 30 test events from this account in an hour.
cURL
curl -X POST "$CORRCONNECT_BASE/v1/developer/apps/$APP_ID/webhooks/$SUBSCRIPTION_ID/test" \
  -H "Cookie: $CORRCONNECT_COOKIE" \
  -H "Origin: $CORRCONNECT_BASE" \
  -H "X-CSRF-Token: $CSRF_TOKEN"
JavaScript
const response = await fetch(`${process.env.CORRCONNECT_BASE}/v1/developer/apps/${process.env.APP_ID}/webhooks/${process.env.SUBSCRIPTION_ID}/test`, {
  method: "POST",
  headers: {
    "Cookie": `${process.env.CORRCONNECT_COOKIE}`,
    "Origin": `${process.env.CORRCONNECT_BASE}`,
    "X-CSRF-Token": `${process.env.CSRF_TOKEN}`,
  },
});
console.log(response.status, await response.json());
Python
import os

import requests

response = requests.post(
    f"{os.environ['CORRCONNECT_BASE']}/v1/developer/apps/{os.environ['APP_ID']}/webhooks/{os.environ['SUBSCRIPTION_ID']}/test",
    headers={"Cookie": f"{os.environ['CORRCONNECT_COOKIE']}", "Origin": f"{os.environ['CORRCONNECT_BASE']}", "X-CSRF-Token": f"{os.environ['CSRF_TOKEN']}"},
    timeout=10,
)
print(response.status_code, response.json())
Swift
import Foundation

let env = ProcessInfo.processInfo.environment
var request = URLRequest(url: URL(string: "\(env["CORRCONNECT_BASE"]!)/v1/developer/apps/\(env["APP_ID"]!)/webhooks/\(env["SUBSCRIPTION_ID"]!)/test")!)
request.httpMethod = "POST"
request.setValue("\(env["CORRCONNECT_COOKIE"]!)", forHTTPHeaderField: "Cookie")
request.setValue("\(env["CORRCONNECT_BASE"]!)", forHTTPHeaderField: "Origin")
request.setValue("\(env["CSRF_TOKEN"]!)", forHTTPHeaderField: "X-CSRF-Token")
let (data, response) = try await URLSession.shared.data(for: request)
print((response as! HTTPURLResponse).statusCode, String(decoding: data, as: UTF8.self))
Response 202
{
  "status": "queued",
  "delivery_id": "whd_0123456789abcdef0123456789abcdef"
}

List recent deliveries

GET/v1/developer/apps/{app_id}/webhooks/{subscription_id}/deliveries

Newest first; limit defaults to 50 (1 to 100). Status and coded errors only, never payloads. Finished deliveries are kept for 30 days.

Auth
Console session cookie
Host
Portal origin only

Parameters

NameInTypeNotes
app_id requiredpathstring
subscription_id requiredpathstring
limitqueryintegerHow many deliveries to return, newest first. 1 to 100 Default 50

Response 200

Successful Response

FieldTypeNotes
data requiredarray of object
data[].id requiredstring
data[].event_id requiredstring
data[].event_type requiredstringOne of activity.created, grant.revoked, webhook.test
data[].status requiredstringOne of pending, delivered, failed
data[].attempts requiredinteger
data[].last_status_codeinteger or null
data[].last_errorstring or null
data[].created_at requiredstring (date-time)
data[].updated_at requiredstring (date-time)

Errors

StatusCodeWhen
401authentication_requiredNo console session cookie was sent.
401invalid_sessionThe session was signed out, replaced or never existed.
401session_expiredThe session is older than its lifetime (12 hours by default).
403email_verification_requiredThe account has not verified its email address yet.
404app_not_foundThe app does not exist or belongs to another account.
404webhook_not_foundThe app has no subscription with this ID.
422validation_failedA field is missing, has the wrong type or is out of range; details.fields says which.
cURL
curl "$CORRCONNECT_BASE/v1/developer/apps/$APP_ID/webhooks/$SUBSCRIPTION_ID/deliveries?limit=50" \
  -H "Cookie: $CORRCONNECT_COOKIE"
JavaScript
const response = await fetch(`${process.env.CORRCONNECT_BASE}/v1/developer/apps/${process.env.APP_ID}/webhooks/${process.env.SUBSCRIPTION_ID}/deliveries?limit=50`, {
  headers: {
    "Cookie": `${process.env.CORRCONNECT_COOKIE}`,
  },
});
console.log(response.status, await response.json());
Python
import os

import requests

response = requests.get(
    f"{os.environ['CORRCONNECT_BASE']}/v1/developer/apps/{os.environ['APP_ID']}/webhooks/{os.environ['SUBSCRIPTION_ID']}/deliveries?limit=50",
    headers={"Cookie": f"{os.environ['CORRCONNECT_COOKIE']}"},
    timeout=10,
)
print(response.status_code, response.json())
Swift
import Foundation

let env = ProcessInfo.processInfo.environment
var request = URLRequest(url: URL(string: "\(env["CORRCONNECT_BASE"]!)/v1/developer/apps/\(env["APP_ID"]!)/webhooks/\(env["SUBSCRIPTION_ID"]!)/deliveries?limit=50")!)
request.setValue("\(env["CORRCONNECT_COOKIE"]!)", forHTTPHeaderField: "Cookie")
let (data, response) = try await URLSession.shared.data(for: request)
print((response as! HTTPURLResponse).statusCode, String(decoding: data, as: UTF8.self))
Response 200
{
  "data": [
    {
      "id": "whd_0123456789abcdef0123456789abcdef",
      "event_id": "evt_0123456789abcdef0123456789abcdef",
      "event_type": "activity.created",
      "status": "delivered",
      "attempts": 1,
      "last_status_code": 200,
      "last_error": null,
      "created_at": "2026-10-09T12:00:00+00:00",
      "updated_at": "2026-10-09T12:00:01+00:00"
    }
  ]
}

Turn a paused subscription back on

POST/v1/developer/apps/{app_id}/webhooks/{subscription_id}/enable

A subscription pauses after 20 failed attempts in a row. This sets it active and resets the count.

Auth
Console session cookie, Origin and X-CSRF-Token
Host
Portal origin only

Parameters

NameInTypeNotes
app_id requiredpathstring
subscription_id requiredpathstring
Origin requiredheaderstringExactly the CorrConnect portal origin.
X-CSRF-Token requiredheaderstringThe latest csrf_token from sign-in or GET /v1/account/me.

Response 200

Successful Response

FieldTypeNotes
id requiredstring
callback_url requiredstring
events requiredarray of string
status requiredstringOne of active, disabled
consecutive_failures requiredinteger
created_at requiredstring (date-time)
updated_at requiredstring (date-time)

Errors

StatusCodeWhen
401authentication_requiredNo console session cookie was sent.
401invalid_sessionThe session was signed out, replaced or never existed.
401session_expiredThe session is older than its lifetime (12 hours by default).
403email_verification_requiredThe account has not verified its email address yet.
403origin_rejectedThe Origin header is not the exact CorrConnect portal origin.
403csrf_rejectedX-CSRF-Token is missing or is not the latest token from GET /v1/account/me.
404app_not_foundThe app does not exist or belongs to another account.
404webhook_not_foundThe app has no subscription with this ID.
429rate_limit_exceededMore than 20 enables by this account in an hour.
cURL
curl -X POST "$CORRCONNECT_BASE/v1/developer/apps/$APP_ID/webhooks/$SUBSCRIPTION_ID/enable" \
  -H "Cookie: $CORRCONNECT_COOKIE" \
  -H "Origin: $CORRCONNECT_BASE" \
  -H "X-CSRF-Token: $CSRF_TOKEN"
JavaScript
const response = await fetch(`${process.env.CORRCONNECT_BASE}/v1/developer/apps/${process.env.APP_ID}/webhooks/${process.env.SUBSCRIPTION_ID}/enable`, {
  method: "POST",
  headers: {
    "Cookie": `${process.env.CORRCONNECT_COOKIE}`,
    "Origin": `${process.env.CORRCONNECT_BASE}`,
    "X-CSRF-Token": `${process.env.CSRF_TOKEN}`,
  },
});
console.log(response.status, await response.json());
Python
import os

import requests

response = requests.post(
    f"{os.environ['CORRCONNECT_BASE']}/v1/developer/apps/{os.environ['APP_ID']}/webhooks/{os.environ['SUBSCRIPTION_ID']}/enable",
    headers={"Cookie": f"{os.environ['CORRCONNECT_COOKIE']}", "Origin": f"{os.environ['CORRCONNECT_BASE']}", "X-CSRF-Token": f"{os.environ['CSRF_TOKEN']}"},
    timeout=10,
)
print(response.status_code, response.json())
Swift
import Foundation

let env = ProcessInfo.processInfo.environment
var request = URLRequest(url: URL(string: "\(env["CORRCONNECT_BASE"]!)/v1/developer/apps/\(env["APP_ID"]!)/webhooks/\(env["SUBSCRIPTION_ID"]!)/enable")!)
request.httpMethod = "POST"
request.setValue("\(env["CORRCONNECT_COOKIE"]!)", forHTTPHeaderField: "Cookie")
request.setValue("\(env["CORRCONNECT_BASE"]!)", forHTTPHeaderField: "Origin")
request.setValue("\(env["CSRF_TOKEN"]!)", forHTTPHeaderField: "X-CSRF-Token")
let (data, response) = try await URLSession.shared.data(for: request)
print((response as! HTTPURLResponse).statusCode, String(decoding: data, as: UTF8.self))
Response 200
{
  "id": "whs_0123456789abcdef0123456789abcdef",
  "callback_url": "https://developer.example/hooks/corrconnect",
  "events": [
    "activity.created",
    "grant.revoked"
  ],
  "status": "active",
  "consecutive_failures": 0,
  "created_at": "2026-10-09T12:00:00+00:00",
  "updated_at": "2026-10-09T12:00:00+00:00"
}

Delete a webhook subscription

DELETE/v1/developer/apps/{app_id}/webhooks/{subscription_id}

Removes the subscription and its delivery log.

Auth
Console session cookie, Origin and X-CSRF-Token
Host
Portal origin only

Parameters

NameInTypeNotes
app_id requiredpathstring
subscription_id requiredpathstring
Origin requiredheaderstringExactly the CorrConnect portal origin.
X-CSRF-Token requiredheaderstringThe latest csrf_token from sign-in or GET /v1/account/me.

Response 200

Successful Response

FieldTypeNotes
status requiredstringOne of deleted

Errors

StatusCodeWhen
401authentication_requiredNo console session cookie was sent.
401invalid_sessionThe session was signed out, replaced or never existed.
401session_expiredThe session is older than its lifetime (12 hours by default).
403email_verification_requiredThe account has not verified its email address yet.
403origin_rejectedThe Origin header is not the exact CorrConnect portal origin.
403csrf_rejectedX-CSRF-Token is missing or is not the latest token from GET /v1/account/me.
404app_not_foundThe app does not exist or belongs to another account.
404webhook_not_foundThe app has no subscription with this ID.
cURL
curl -X DELETE "$CORRCONNECT_BASE/v1/developer/apps/$APP_ID/webhooks/$SUBSCRIPTION_ID" \
  -H "Cookie: $CORRCONNECT_COOKIE" \
  -H "Origin: $CORRCONNECT_BASE" \
  -H "X-CSRF-Token: $CSRF_TOKEN"
JavaScript
const response = await fetch(`${process.env.CORRCONNECT_BASE}/v1/developer/apps/${process.env.APP_ID}/webhooks/${process.env.SUBSCRIPTION_ID}`, {
  method: "DELETE",
  headers: {
    "Cookie": `${process.env.CORRCONNECT_COOKIE}`,
    "Origin": `${process.env.CORRCONNECT_BASE}`,
    "X-CSRF-Token": `${process.env.CSRF_TOKEN}`,
  },
});
console.log(response.status, await response.json());
Python
import os

import requests

response = requests.delete(
    f"{os.environ['CORRCONNECT_BASE']}/v1/developer/apps/{os.environ['APP_ID']}/webhooks/{os.environ['SUBSCRIPTION_ID']}",
    headers={"Cookie": f"{os.environ['CORRCONNECT_COOKIE']}", "Origin": f"{os.environ['CORRCONNECT_BASE']}", "X-CSRF-Token": f"{os.environ['CSRF_TOKEN']}"},
    timeout=10,
)
print(response.status_code, response.json())
Swift
import Foundation

let env = ProcessInfo.processInfo.environment
var request = URLRequest(url: URL(string: "\(env["CORRCONNECT_BASE"]!)/v1/developer/apps/\(env["APP_ID"]!)/webhooks/\(env["SUBSCRIPTION_ID"]!)")!)
request.httpMethod = "DELETE"
request.setValue("\(env["CORRCONNECT_COOKIE"]!)", forHTTPHeaderField: "Cookie")
request.setValue("\(env["CORRCONNECT_BASE"]!)", forHTTPHeaderField: "Origin")
request.setValue("\(env["CSRF_TOKEN"]!)", forHTTPHeaderField: "X-CSRF-Token")
let (data, response) = try await URLSession.shared.data(for: request)
print((response as! HTTPURLResponse).statusCode, String(decoding: data, as: UTF8.self))
Response 200
{
  "status": "deleted"
}