Certifyos Webhooks API

APIs for managing webhooks

Business capability
Webhook & Event Subscription Management BC-4270.80

Operations 2

POST /webhooks/cloud-deliver Deliver a webhook event to a partner URL #
POST /webhooks/dlq/drain Drain the webhook delivery DLQ #

Work with this as data

Every API here is available over the APIs.io API and to AI agents over MCP.

MCP server

One button, every client — Claude, Cursor, VS Code and the rest.

https://apis.io/mcp

Tools for apis

7 MCP tools reach this
  • find_apisBrowse and filter every API in the catalog.
  • get_api_artifactsOne API's artifacts, grouped by type.
  • get_openapiThe primary OpenAPI for this API.
  • find_similar_apisAPIs that look like this one.
  • apis_io_searchSTART HERE — APIs, providers and tags for one query, each with its total.
  • resolveTurn a domain, URL or GitHub org into the provider it belongs to.
  • find_cohortsEvery scored population of providers in the catalog.
All 92 tools →

Call it yourself

curl for this page
This API
curl "https://apis.io/api/v1/apis/certifyos:certifyos-webhooks-api"
All apis
curl "https://apis.io/api/v1/apis?limit=25"

Discovery needs no key. Ratings and market analysis are Pro.

Get an API key

Free tier, no form to fill in. Signing in shares your email address with us — we store it to create your key and to recognise you if you sign in with another provider. See our Privacy Policy and Terms.

A second provider on the same verified email joins the account you already have.

OpenAPI Specification

certifyos-webhooks-api-openapi.yml Raw ↑
openapi: 3.2.0
info:
  description: API for Certify application
  title: Certify API Layer Webhooks API
  version: 1.0.0
servers:
- url: http://localhost:9000
  description: Local Development Server
- url: https://api-service.staging.certifyos.com
  description: Staging Server
- url: https://api-service.internal.certifyos.com
  description: Internal Server
- url: https://api-service.test.certifyos.com
  description: Test Server
- url: https://api-service.demo.certifyos.com
  description: Demo Server
- url: https://api-service.certifyos.com
  description: Production Server
tags:
- name: Webhooks
  description: APIs for managing webhooks
paths:
  /webhooks/cloud-deliver:
    post:
      summary: Deliver a webhook event to a partner URL
      description: 'What it does: Executes an outbound HTTP POST to the partner URL in the request body, delivering a webhook event payload on behalf of the credentialing platform. The service sets an Idempotency-Key header (SHA-256 over messageId, webhookId, and eventType) on the outbound request and, when OAuth is configured, mints an Authorization: Bearer token before dispatching.


        When to use: This endpoint is an internal callback invoked by the Cloud Tasks delivery queue; it is not intended to be called directly by external API consumers.


        How (preconditions and authentication): The request must include the X-Credentialing-Api-Key header with a valid key; an absent or invalid key returns 401. The body must be a JSON object containing at minimum webhookUrl (the partner''s receiving endpoint) and eventType. Optional body fields include webhookId, messageId, headers (additional outbound headers), eventPayload (the event body sent to the partner), and oauthConfiguration (when absent or blank, the service falls back to static-header authentication mode). The tenant-id and user-id headers are forwarded as delivery context. A 400 is returned when the body is null or when webhookUrl or eventType is missing or blank; supply all required fields to resolve.


        What you get back: On success (200) the partner endpoint returned a 2xx response and the body is {"message": "Webhook delivered successfully"}. The service performs internal exponential-backoff retries (configurable maximum) before giving up; if all retries are exhausted it returns 502 with a body of the form {"message": ..., "statusCode": ..., "error": ...}. When the dead-letter queue is enabled, an exhausted event is instead published to the DLQ and acknowledged with a 200 {"message": ..., "dlq": true} body so it can be replayed later.'
      operationId: webhookDeliveryCallback
      tags:
      - Webhooks
      parameters:
      - description: Zero-indexed Cloud Tasks retry count, set by Cloud Tasks on each attempt. Used to detect the final attempt and route exhausted events to the DLQ. Defaults to 0 when absent or unparseable.
        name: X-CloudTasks-TaskRetryCount
        in: header
        schema:
          type: string
      - description: Tenant identifier forwarded to the delivery service as delivery context. Optional; when omitted no tenant context is attached to the delivery.
        name: tenant-id
        in: header
        schema:
          type: string
      - description: User identifier for service-to-service call context. Optional; accepted for request-context propagation.
        name: user-id
        in: header
        schema:
          type: string
      requestBody:
        description: Webhook delivery task payload enqueued by the Cloud Tasks delivery queue. webhookUrl and eventType are required; all other fields are optional.
        content:
          application/json:
            examples:
              deliverWebhook:
                value:
                  messageId: msg_01HZX8N4QK2P7Y3B
                  webhookId: wh_9f8c2b1a4d6e
                  eventType: provider.credentialing.completed
                  webhookUrl: https://partner.example.com/webhooks/certifyos
                  headers:
                    X-Partner-Signature: v1=abc123
                  eventPayload:
                    providerId: prov_12345
                    npi: '1234567890'
                    status: COMPLETED
                    completedAt: '2026-07-21T14:32:00Z'
                  oauthConfiguration:
                    tokenUrl: https://auth.example.com/oauth/token
                    clientId: certifyos-webhooks
                    clientSecret: s3cr3t
                    scope: webhooks:write
            schema:
              type: object
              description: Free-form Cloud Tasks delivery payload; see field descriptions above.
        required: true
      responses:
        '200':
          description: The partner endpoint acknowledged the event with a 2xx response, or the exhausted event was published to the dead-letter queue for later replay.
          content:
            application/json:
              examples:
                delivered:
                  summary: Partner acknowledged the event
                  value:
                    message: Webhook delivered successfully
                publishedToDlq:
                  summary: Retries exhausted; event published to the DLQ
                  value:
                    message: Webhook delivery failed - event published to DLQ
                    dlq: true
        '400':
          description: 'Two distinct 400 shapes are possible. (1) The task payload was well-formed JSON but missing or malformed: the body was null, or webhookUrl or eventType was absent or blank; resend the task with both required fields populated. This returns the flat {"message": ...} body shown in the missingRequiredFields example. (2) The request body was not valid JSON: this is caught by the framework before the handler runs and returns the structured ApiError shape shown in the malformedJson example instead.'
          content:
            application/json:
              examples:
                missingRequiredFields:
                  summary: webhookUrl or eventType absent or blank
                  value:
                    message: 'Missing required fields: webhookUrl, eventType'
                malformedJson:
                  summary: Request body is not valid JSON
                  value:
                    errors:
                    - httpStatus: 400
                      reason: VALIDATION_ERROR
                      title: Invalid request data
                      detail: Unexpected character (...) - malformed JSON
        '401':
          description: The X-Credentialing-Api-Key header was missing or did not match the configured key. Supply the correct credentialing API key and retry.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiError'
        '502':
          description: The partner endpoint returned a non-2xx response or was unreachable for this delivery attempt. This is returned both on non-final attempts (Cloud Tasks will retry) and on the final attempt when the event could not be published to the DLQ (e.g. the DLQ is disabled, or publishing itself failed). Inspect statusCode and error, verify the partner endpoint, then allow Cloud Tasks to retry.
          content:
            application/json:
              examples:
                deliveryFailed:
                  value:
                    message: Webhook delivery failed
                    statusCode: 503
                    error: Service Unavailable
  /webhooks/dlq/drain:
    post:
      summary: Drain the webhook delivery DLQ
      description: 'Pulls failed webhook delivery events from the DLQ Pub/Sub subscription and re-enqueues them as Cloud Tasks for redelivery. Supports filtering by webhookId and tenantId. Use dryRun=true to preview without re-enqueuing. Requires the webhook:create permission and a tenant-id header. Note: the drain does a single bounded pull (maxMessages) and filters client-side, so on a DLQ dominated by other tenants a tenant-scoped drain may pull mostly non-matching messages and need to be re-run; increase maxMessages and/or repeat until the summary shows requeued==0 with an empty pull.'
      operationId: drainWebhookDlq
      tags:
      - Webhooks
      parameters:
      - description: 'Caller''s tenant identifier. Required. The drain is scoped to this tenant: it overrides any tenantId supplied in the request body so callers cannot drain other tenants'' failed webhooks.'
        in: header
        required: true
        name: tenant-id
        schema:
          type: string
      requestBody:
        description: Drain parameters. All fields are optional; an empty body drains up to 100 messages to the default delivery queue without dry-run. A caller-supplied tenantId is ignored and replaced with the tenant-id header for security.
        content:
          application/json:
            examples:
              drainForWebhook:
                summary: Re-enqueue failed events for one webhook
                value:
                  maxMessages: 200
                  webhookId: wh_9f8c2b1a4d6e
                  targetQueue: webhook-delivery
                  dryRun: false
              dryRunPreview:
                summary: Inspect matching events without re-enqueuing
                value:
                  maxMessages: 100
                  dryRun: true
            schema:
              $ref: '#/components/schemas/DrainDlqRequest'
        required: true
      responses:
        '200':
          description: 'Drain summary: counts of messages pulled, re-enqueued, filtered out, and failed, plus per-message results. On a dry run, matching events are reported with status DRY_RUN and none are re-enqueued.'
          content:
            application/json:
              examples:
                drainSummary:
                  value:
                    totalPulled: 12
                    requeued: 8
                    filtered: 3
                    failed: 1
                    results:
                    - messageId: '7654321098765432'
                      webhookId: wh_9f8c2b1a4d6e
                      tenantId: tenant_acme
                      status: REQUEUED
                      detail: to webhook-delivery
                    - messageId: '7654321098765433'
                      webhookId: wh_2a1b3c4d5e6f
                      tenantId: tenant_other
                      status: FILTERED
                      detail: did not match filter
              schema:
                $ref: '#/components/schemas/DrainDlqResponse'
        '400':
          description: 'Two distinct 400 shapes are possible. (1) The requested targetQueue is not an allowed webhook delivery queue: re-run with an allowed queue name (or omit targetQueue to use the default delivery queue); this returns the flat {"message": ...} body shown in the invalidTargetQueue example. (2) The request failed Bean Validation — the tenant-id header is missing, or maxMessages is outside the 1-1000 range: this returns the structured ApiError shape shown in the validationError example instead.'
          content:
            application/json:
              examples:
                invalidTargetQueue:
                  summary: targetQueue is not an allowed delivery queue
                  value:
                    message: 'Invalid targetQueue. Allowed values: [webhook-delivery, webhook-delivery-overflow]'
                validationError:
                  summary: Bean Validation failure (missing tenant-id header, or maxMessages outside 1-1000)
                  value:
                    errors:
                    - httpStatus: 400
                      reason: VALIDATION_ERROR
                      title: 'Validation failed for field: drain.tenantId'
                      detail: must not be null
        '401':
          description: Authentication is required or the JWT is missing/invalid. Retry with a valid bearer token.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiError'
        '403':
          description: The caller lacks the webhook:create permission required to drain the DLQ.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiError'
        '502':
          description: Pulling messages from the DLQ Pub/Sub subscription failed (for example, Pub/Sub was unavailable). Distinguishes a transient outage from an empty queue; retry the drain.
          content:
            application/json:
              examples:
                drainFailed:
                  value:
                    message: 'Failed to drain DLQ: Pub/Sub subscription unavailable'
      security:
      - jwt: []
components:
  schemas:
    ApiError:
      type: object
      description: Standard API error response containing a list of error objects
      properties:
        errors:
          type: array
          items:
            $ref: '#/components/schemas/ErrorObject'
          description: List of error objects describing validation or processing failures
    DrainResult:
      type: object
      properties:
        messageId:
          type: string
          examples:
          - '7654321098765432'
          description: Pub/Sub message id of the DLQ message.
        webhookId:
          type: string
          examples:
          - wh_9f8c2b1a4d6e
          description: Webhook id extracted from the failed event, when available.
        tenantId:
          type: string
          examples:
          - tenant_acme
          description: Tenant id extracted from the failed event, when available.
        status:
          type: string
          enum:
          - REQUEUED
          - FILTERED
          - DRY_RUN
          - FAILED
          examples:
          - REQUEUED
          description: 'Outcome for this message. One of: REQUEUED (re-enqueued to the target queue), FILTERED (did not match the filter), DRY_RUN (matched but not re-enqueued because dryRun was true), FAILED (could not be re-enqueued or the payload was malformed).'
        detail:
          type: string
          examples:
          - to webhook-delivery
          description: Human-readable detail for the outcome, such as the target queue or the failure reason.
    DrainDlqResponse:
      type: object
      properties:
        totalPulled:
          type: integer
          format: int32
          examples:
          - 12
          description: Total messages pulled from the DLQ subscription in this drain.
        requeued:
          type: integer
          format: int32
          examples:
          - 8
          description: Messages successfully re-enqueued to the target Cloud Tasks queue.
        filtered:
          type: integer
          format: int32
          examples:
          - 3
          description: Messages skipped because they did not match the webhookId/tenantId filter.
        failed:
          type: integer
          format: int32
          examples:
          - 1
          description: Messages that failed to re-enqueue; they remain in the DLQ and are retried on the next drain.
        results:
          type: array
          items:
            $ref: '#/components/schemas/DrainResult'
          description: Per-message outcome details for the messages pulled in this drain.
    ErrorObject:
      type: object
      description: Individual error object containing details about a specific validation or processing error
      properties:
        httpStatus:
          type: integer
          format: int32
          examples:
          - 400
          description: HTTP status code for this error
        reason:
          type: string
          examples:
          - VALIDATION_ERROR
          description: Error reason/code
        title:
          type: string
          examples:
          - 'Validation failed for field: eventTypes'
          description: Error title/summary
        detail:
          type: string
          examples:
          - eventTypes is required and cannot be empty or null
          description: Detailed error message
        rejectedValue:
          type: string
          description: The value that was rejected, omitted for sensitive fields such as SSN
    DrainDlqRequest:
      type: object
      properties:
        maxMessages:
          type: integer
          format: int32
          examples:
          - 100
          description: Maximum number of messages to pull from the DLQ in this invocation. Integer between 1 and 1000; defaults to 100 when omitted.
          maximum: 1000
          minimum: 1
        webhookId:
          type: string
          examples:
          - wh_9f8c2b1a4d6e
          description: 'Optional filter: only re-enqueue events for this webhook id. When omitted, events for all webhooks (within the tenant) are eligible.'
        tenantId:
          type: string
          description: Optional in the payload but always overridden by the tenant-id header for security; a caller-supplied value that differs from the header is ignored.
        targetQueue:
          type: string
          examples:
          - webhook-delivery
          description: Optional Cloud Tasks queue to re-enqueue matching events to. Must be an allowed webhook delivery queue; defaults to the primary webhook-delivery queue when omitted.
        dryRun:
          type: boolean
          examples:
          - false
          description: When true, pull and inspect matching messages but do not re-enqueue them (messages are nacked back to the subscription). Defaults to false.
  securitySchemes:
    jwt:
      type: http
      description: JWT Authentication - Provide only the raw token without Bearer prefix
      scheme: bearer
      bearerFormat: JWT
x-provenance:
  method: harvested
  publisher: Certifyos
  source: https://api-service.internal.certifyos.com/openapi.json
  note: split by refine-openapis from the provider's own published document, verified by fetch (openapi/_original/*.fetch.json);
    stamped by stamp-openapi-provenance.py (roadmap#793)