Skip to content
DocsSDKWebhooks

Webhooks

Updated Jul 2026·1 min

Subscribe your systems to workspace events. Every payload is signed; verifyWebhookSignature() rejects tampered or replayed deliveries.

The event catalog: every event the workspace publishes, with listeners and Create flow actions.The event catalog: every event the workspace publishes, with listeners and Create flow actions.

Quick example

Type-checked against @medalsocial/sdk v1.3.0 (2026-07-20). A release that changes a signature fails this page's build.

ts
import { Medal, verifyWebhookSignature } from '@medalsocial/sdk';

const medal = new Medal(process.env.MEDAL_API_KEY as string);

// Register an endpoint (idempotency key recommended for writes)
const { data: endpoint } = await medal.webhooks.create(
  {
    name: 'CRM sync',
    url: 'https://example.com/hooks/medal',
    event_types: ['contact.created', 'post.published'],
  },
  { idempotencyKey: 'crm-sync-setup-1' }
);

// Send a test delivery
await medal.webhooks.test(endpoint.id);

// In your receiver: verify the signature before trusting the payload
export async function handler(rawBody: string, signature: string, timestamp: string) {
  const event = await verifyWebhookSignature({
    payload: rawBody,
    signature,
    timestamp,
    secret: process.env.MEDAL_WEBHOOK_SECRET as string,
  });
  console.log(event.type);
}

medal.webhooks.list()

List all webhook endpoints in the workspace.

ts
list(): Promise<ApiResponse<WebhookEndpoint[]>>
Added in v1.3.0

medal.webhooks.create()

Create a webhook endpoint. Returns HTTP 201.

**The response's `data.secret` contains the signing secret EXACTLY ONCE.**
It can never be retrieved again — store it securely immediately. You need
it to verify the `X-Medal-Signature` header on incoming deliveries (see
`verifyWebhookSignature`).

`secret` is typed optional because an idempotent replay (retrying with the
same `Idempotency-Key`, `X-Idempotent-Replayed: true`) returns the existing
endpoint WITHOUT the secret — handle that case (rotate if you lost it).

ts
create(input: CreateWebhookInput, options?: RequestOptions): Promise<ApiResponse<WebhookEndpoint>>
Added in v1.3.0

medal.webhooks.get()

Get a webhook endpoint by ID.

ts
get(id: string): Promise<ApiResponse<WebhookEndpoint>>
Added in v1.3.0

medal.webhooks.update()

Update a webhook endpoint (name, url, event types, filters, enabled).

ts
update(id: string, input: UpdateWebhookInput, options?: RequestOptions): Promise<ApiResponse<WebhookEndpoint>>
Added in v1.3.0

medal.webhooks.delete()

Permanently delete a webhook endpoint (stops all outbound deliveries).
Capability-scoped tokens must pass `idempotencyKey` — the API requires
`Idempotency-Key` + `X-Capability-Confirmation` for direct capability
grants on this route. API keys with legacy scopes may omit it.

ts
delete(id: string, options?: RequestOptions): Promise<ApiResponse<WebhookDeleteResult>>
Added in v1.3.0

medal.webhooks.deliveries()

List recent deliveries for an endpoint (most recent first).

ts
deliveries(id: string, options?: ListDeliveriesOptions): Promise<ApiResponse<WebhookDelivery[]>>
Added in v1.3.0

medal.webhooks.test()

Queue a signed `test.ping` delivery to the endpoint. Returns HTTP 202.

ts
test(id: string): Promise<ApiResponse<WebhookTestResult>>
Added in v1.3.0

verifyWebhookSignature()

Verifies a delivery came from Medal and is fresh: checks the HMAC signature against your endpoint secret and rejects stale timestamps.

ts
declare function verifyWebhookSignature(input: VerifyWebhookSignatureInput): Promise<WebhookEvent>
Added in v1.3.0