{
  "openapi": "3.1.1",
  "info": {
    "title": "Datasource Background Check Partner API",
    "description": "Order and track background checks for the churches you serve, from your own platform. Connect a church once\n(on a page we host, or with one API call), then order checks by name and email: we email each applicant to\ncollect their details and consent, and send you signed webhooks as checks progress. You never handle SSNs,\ndates of birth, card numbers or passwords.\n\nFull guide: [/docs/guide.md](https://partners.cbgc.io/docs/guide.md) · error catalog:\n[/docs/errors.md](https://partners.cbgc.io/docs/errors.md) · for AI agents: [/llms.txt](https://partners.cbgc.io/llms.txt)\nand [/llms-full.txt](https://partners.cbgc.io/llms-full.txt) (everything in one file).\n\n## How it works (start here)\n| Environment | Base URL | API keys | Checks |\n|---|---|---|---|\n| Test (sandbox) | `https://dev.partners.cbgc.io` | `pk_test_...` | Not real; nothing is charged |\n| Production | `https://partners.cbgc.io` | `pk_live_...` | Real and billed |\n\n1. `GET /status` with your test key: `200` means it works.\n2. `PUT /settings` with your webhook URL and connect return URL.\n3. Connect a church: `POST /connect/sessions` and send its browser to `connectUrl` (or `POST /clients`).\n4. `GET /products`, then `POST /clients/{clientRef}/checks` with a `level` (1 to 6).\n5. Handle `check.updated` webhooks, and reconcile nightly with `GET /clients/{clientRef}/checks`.\n\n## Concepts\n- **Client**: one church. Address it as `{clientRef}`: our `clientId` or your own `externalClientId`.\n- **Check**: one background check for one applicant, with a contract-stable `status` and a finer `stage`.\n  Use `externalIdentifier` (your id, unique across all your clients) to match it to your records.\n- **Level**: products are levels 1 to 6 (`GET /products`). We translate a level to each church's own product.\n- **Batch**: up to 500 checks in one call. **Connect session**: one run of the hosted connect page for one church.\n\n## Authentication\nSend your API key on every request as `X-Api-Key: <key>` or `Authorization: Bearer <key>`. Keys are issued by Datasource,\nshown once, and belong in a secret manager (never in browser or app code). You can have two active keys, so you\ncan rotate without downtime. Repeated invalid keys from one IP address are answered with `429 rate_limited` for a\nminute; requests with a valid key are never throttled that way.\n\n## Conventions\n- JSON in and out, camelCase names, UTC ISO 8601 timestamps.\n- Errors are [RFC 9457 problem details](https://www.rfc-editor.org/rfc/rfc9457) with a stable `code` and a `type`\n  link to its catalog entry. Branch on `code`. Two documented bodies aren't problem details: `409 duplicate_check`\n  (the existing check) and `502` on ordering (the `PENDING` check).\n- Always send an `externalIdentifier` and reuse it on retries: a retry never creates a second check or bill.\n- Ignore fields and webhook events you don't know. Changes are additive; a breaking change gets 90 days' notice in\n  the changelog (`/docs/changelog.md`).\n\n## Compliance\nDatasource is the consumer reporting agency. The church is the end user and makes, and is responsible for, all decisions,\nincluding adverse action, which is handled in its portal.",
    "version": "1.0"
  },
  "servers": [
    {
      "url": "https://partners.cbgc.io",
      "description": "Production"
    },
    {
      "url": "https://dev.partners.cbgc.io",
      "description": "Test (sandbox)"
    }
  ],
  "paths": {
    "/clients": {
      "post": {
        "tags": [
          "Clients"
        ],
        "summary": "Create a client",
        "description": "Provisions one of your customers as a client under your account. Each customer needs its own client before\nyou can order checks for them.\n\nA portal login is created for the primary contact, and Datasource emails them their username and a temporary password\n(changed at first sign-in). No password is ever returned to you; `welcomeEmailSent` says whether the email went.\n\nThe new client starts with your account's packages and settings.\n\nSend your own id for the customer as `externalClientId` and you never have to store ours: every\n`{clientRef}` path accepts it. It is unique among your clients (`409` if already used).",
        "operationId": "CreateClient",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreateClientRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "201": {
            "description": "The client was created, with a portal login for its primary contact. The contact gets their username and a temporary password by email from Datasource; no password is ever returned to you. `welcomeEmailSent` is false if that email couldn't be sent (contact support).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CreateClientResponse"
                }
              }
            }
          },
          "400": {
            "description": "`validation_failed`: a required field is missing or too long. `errors` names each field. Nothing was created; fix the request and send it again.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/HttpValidationProblemDetails"
                },
                "examples": {
                  "validation_failed": {
                    "summary": "validation_failed",
                    "value": {
                      "type": "https://partners.cbgc.io/docs/errors.md#validation_failed",
                      "title": "One or more validation errors occurred.",
                      "status": 400,
                      "errors": {
                        "primaryContactEmail": [
                          "primaryContactEmail is required."
                        ]
                      },
                      "code": "validation_failed"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "`invalid_api_key`: the API key is missing, invalid or revoked, or it's a test key on production (or the reverse). Don't retry until the key is fixed.",
            "content": {
              "application/problem+json": {
                "examples": {
                  "invalid_api_key": {
                    "summary": "invalid_api_key",
                    "value": {
                      "type": "https://partners.cbgc.io/docs/errors.md#invalid_api_key",
                      "title": "Invalid API key",
                      "status": 401,
                      "detail": "The API key is missing, invalid or revoked.",
                      "code": "invalid_api_key"
                    }
                  }
                }
              }
            }
          },
          "409": {
            "description": "`external_client_id_in_use`: another of your clients already has this `externalClientId`. Nothing was created; use the existing client (`GET /clients?externalClientId=`).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "external_client_id_in_use": {
                    "summary": "external_client_id_in_use",
                    "value": {
                      "type": "https://partners.cbgc.io/docs/errors.md#external_client_id_in_use",
                      "title": "External client id in use",
                      "status": 409,
                      "detail": "Another of your clients already uses externalClientId 'org-1042'.",
                      "code": "external_client_id_in_use"
                    }
                  }
                }
              }
            }
          },
          "502": {
            "description": "`upstream_unavailable`: the screening provider didn't answer. Nothing was recorded on our side; it's safe to retry.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "upstream_unavailable": {
                    "summary": "upstream_unavailable",
                    "value": {
                      "type": "https://partners.cbgc.io/docs/errors.md#upstream_unavailable",
                      "title": "Screening provider unavailable",
                      "status": 502,
                      "detail": "The screening provider did not accept the request. Nothing was created; it is safe to retry.",
                      "code": "upstream_unavailable"
                    }
                  }
                }
              }
            }
          },
          "429": {
            "description": "`rate_limited`: too many requests with an invalid API key from your IP address. Wait the number of seconds in `Retry-After`. Requests with a valid key are never throttled this way.",
            "content": {
              "application/problem+json": {
                "examples": {
                  "rate_limited": {
                    "summary": "rate_limited",
                    "value": {
                      "type": "https://partners.cbgc.io/docs/errors.md#rate_limited",
                      "title": "Too many requests",
                      "status": 429,
                      "detail": "Too many requests with an invalid API key from your IP address. Wait for the number of seconds in the Retry-After header.",
                      "code": "rate_limited"
                    }
                  }
                }
              }
            }
          }
        }
      },
      "get": {
        "tags": [
          "Clients"
        ],
        "summary": "List clients",
        "description": "Returns every client under your account, oldest first. Pass `externalClientId` to look up one client by your own id: the list then holds that client, or nothing.",
        "operationId": "ListClients",
        "parameters": [
          {
            "name": "externalClientId",
            "in": "query",
            "description": "Return only the client with this `externalClientId` (your own id for the customer, at most 100 characters). The list then has 0 or 1 items.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Your clients, oldest first. With `externalClientId`, a list of 0 or 1 items.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/ClientResponse"
                  }
                }
              }
            }
          },
          "401": {
            "description": "`invalid_api_key`: the API key is missing, invalid or revoked, or it's a test key on production (or the reverse). Don't retry until the key is fixed.",
            "content": {
              "application/problem+json": {
                "examples": {
                  "invalid_api_key": {
                    "summary": "invalid_api_key",
                    "value": {
                      "type": "https://partners.cbgc.io/docs/errors.md#invalid_api_key",
                      "title": "Invalid API key",
                      "status": 401,
                      "detail": "The API key is missing, invalid or revoked.",
                      "code": "invalid_api_key"
                    }
                  }
                }
              }
            }
          },
          "429": {
            "description": "`rate_limited`: too many requests with an invalid API key from your IP address. Wait the number of seconds in `Retry-After`. Requests with a valid key are never throttled this way.",
            "content": {
              "application/problem+json": {
                "examples": {
                  "rate_limited": {
                    "summary": "rate_limited",
                    "value": {
                      "type": "https://partners.cbgc.io/docs/errors.md#rate_limited",
                      "title": "Too many requests",
                      "status": 429,
                      "detail": "Too many requests with an invalid API key from your IP address. Wait for the number of seconds in the Retry-After header.",
                      "code": "rate_limited"
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/clients/{clientRef}": {
      "get": {
        "tags": [
          "Clients"
        ],
        "summary": "Get a client",
        "description": "Returns one of your clients.",
        "operationId": "GetClient",
        "parameters": [
          {
            "name": "clientRef",
            "in": "path",
            "description": "The client: our `clientId` (`cl_…`) or your own `externalClientId` (at most 100 characters; URL-encode it). Both resolve only among your own clients; anything else is `404 client_not_found`.",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The client.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ClientResponse"
                }
              }
            }
          },
          "401": {
            "description": "`invalid_api_key`: the API key is missing, invalid or revoked, or it's a test key on production (or the reverse). Don't retry until the key is fixed.",
            "content": {
              "application/problem+json": {
                "examples": {
                  "invalid_api_key": {
                    "summary": "invalid_api_key",
                    "value": {
                      "type": "https://partners.cbgc.io/docs/errors.md#invalid_api_key",
                      "title": "Invalid API key",
                      "status": 401,
                      "detail": "The API key is missing, invalid or revoked.",
                      "code": "invalid_api_key"
                    }
                  }
                }
              }
            }
          },
          "404": {
            "description": "`client_not_found`: no client with that `clientId` or `externalClientId` exists under your account. Don't retry.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "client_not_found": {
                    "summary": "client_not_found",
                    "value": {
                      "type": "https://partners.cbgc.io/docs/errors.md#client_not_found",
                      "title": "Client not found",
                      "status": 404,
                      "detail": "No client 'org-1042' exists under your account.",
                      "code": "client_not_found"
                    }
                  }
                }
              }
            }
          },
          "429": {
            "description": "`rate_limited`: too many requests with an invalid API key from your IP address. Wait the number of seconds in `Retry-After`. Requests with a valid key are never throttled this way.",
            "content": {
              "application/problem+json": {
                "examples": {
                  "rate_limited": {
                    "summary": "rate_limited",
                    "value": {
                      "type": "https://partners.cbgc.io/docs/errors.md#rate_limited",
                      "title": "Too many requests",
                      "status": 429,
                      "detail": "Too many requests with an invalid API key from your IP address. Wait for the number of seconds in the Retry-After header.",
                      "code": "rate_limited"
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/clients/{clientRef}/settings": {
      "get": {
        "tags": [
          "Clients"
        ],
        "summary": "Get a client's settings",
        "description": "Returns the client's own settings and the values that apply to it (its own, else your account's defaults).",
        "operationId": "GetClientSettings",
        "parameters": [
          {
            "name": "clientRef",
            "in": "path",
            "description": "The client: our `clientId` (`cl_…`) or your own `externalClientId` (at most 100 characters; URL-encode it). Both resolve only among your own clients; anything else is `404 client_not_found`.",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ClientSettingsResponse"
                }
              }
            }
          },
          "401": {
            "description": "`invalid_api_key`: the API key is missing, invalid or revoked, or it's a test key on production (or the reverse). Don't retry until the key is fixed.",
            "content": {
              "application/problem+json": {
                "examples": {
                  "invalid_api_key": {
                    "summary": "invalid_api_key",
                    "value": {
                      "type": "https://partners.cbgc.io/docs/errors.md#invalid_api_key",
                      "title": "Invalid API key",
                      "status": 401,
                      "detail": "The API key is missing, invalid or revoked.",
                      "code": "invalid_api_key"
                    }
                  }
                }
              }
            }
          },
          "404": {
            "description": "Not Found",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "429": {
            "description": "`rate_limited`: too many requests with an invalid API key from your IP address. Wait the number of seconds in `Retry-After`. Requests with a valid key are never throttled this way.",
            "content": {
              "application/problem+json": {
                "examples": {
                  "rate_limited": {
                    "summary": "rate_limited",
                    "value": {
                      "type": "https://partners.cbgc.io/docs/errors.md#rate_limited",
                      "title": "Too many requests",
                      "status": 429,
                      "detail": "Too many requests with an invalid API key from your IP address. Wait for the number of seconds in the Retry-After header.",
                      "code": "rate_limited"
                    }
                  }
                }
              }
            }
          }
        }
      },
      "put": {
        "tags": [
          "Clients"
        ],
        "summary": "Update a client's settings",
        "description": "Sets one client's own settings. This call replaces them; send the complete set.\n\n`checkValidityYears` (1 to 5) is how long this client's `CLEAR` or `REVIEW` checks stay valid: their\n`expirationDate` is completion plus that many years, and until then ordering the same applicant again needs\n`forceReorder`. Null or omitted uses your account's default (`checkValidityYears` in `PUT /settings`). A\nchange applies to checks completed after it.",
        "operationId": "UpdateClientSettings",
        "parameters": [
          {
            "name": "clientRef",
            "in": "path",
            "description": "The client: our `clientId` (`cl_…`) or your own `externalClientId` (at most 100 characters; URL-encode it). Both resolve only among your own clients; anything else is `404 client_not_found`.",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ClientSettingsRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ClientSettingsResponse"
                }
              }
            }
          },
          "400": {
            "description": "Bad Request",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/HttpValidationProblemDetails"
                }
              }
            }
          },
          "401": {
            "description": "`invalid_api_key`: the API key is missing, invalid or revoked, or it's a test key on production (or the reverse). Don't retry until the key is fixed.",
            "content": {
              "application/problem+json": {
                "examples": {
                  "invalid_api_key": {
                    "summary": "invalid_api_key",
                    "value": {
                      "type": "https://partners.cbgc.io/docs/errors.md#invalid_api_key",
                      "title": "Invalid API key",
                      "status": 401,
                      "detail": "The API key is missing, invalid or revoked.",
                      "code": "invalid_api_key"
                    }
                  }
                }
              }
            }
          },
          "404": {
            "description": "Not Found",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "429": {
            "description": "`rate_limited`: too many requests with an invalid API key from your IP address. Wait the number of seconds in `Retry-After`. Requests with a valid key are never throttled this way.",
            "content": {
              "application/problem+json": {
                "examples": {
                  "rate_limited": {
                    "summary": "rate_limited",
                    "value": {
                      "type": "https://partners.cbgc.io/docs/errors.md#rate_limited",
                      "title": "Too many requests",
                      "status": 429,
                      "detail": "Too many requests with an invalid API key from your IP address. Wait for the number of seconds in the Retry-After header.",
                      "code": "rate_limited"
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/products": {
      "get": {
        "tags": [
          "Products"
        ],
        "summary": "List product levels",
        "description": "Returns every product level (1 to 6) you can order, sorted by level, with its name, description and\nbase price. Send the `level` when ordering a check; you never need to know which product that maps to.\nPrices are base prices: third-party fees are added (\"Plus pass-through fees.\"). Fees can differ per\nclient, so use `GET /clients/{clientRef}/products` to show a client's own prices.",
        "operationId": "ListProducts",
        "responses": {
          "200": {
            "description": "Every level, 1 to 6, with its base price from your account's products. Show `feesNote` next to prices.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/ProductResponse"
                  }
                }
              }
            }
          },
          "401": {
            "description": "`invalid_api_key`: the API key is missing, invalid or revoked, or it's a test key on production (or the reverse). Don't retry until the key is fixed.",
            "content": {
              "application/problem+json": {
                "examples": {
                  "invalid_api_key": {
                    "summary": "invalid_api_key",
                    "value": {
                      "type": "https://partners.cbgc.io/docs/errors.md#invalid_api_key",
                      "title": "Invalid API key",
                      "status": 401,
                      "detail": "The API key is missing, invalid or revoked.",
                      "code": "invalid_api_key"
                    }
                  }
                }
              }
            }
          },
          "502": {
            "description": "`upstream_unavailable`: the screening provider didn't answer. Safe to retry.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "upstream_unavailable": {
                    "summary": "upstream_unavailable",
                    "value": {
                      "type": "https://partners.cbgc.io/docs/errors.md#upstream_unavailable",
                      "title": "Screening provider unavailable",
                      "status": 502,
                      "detail": "The screening provider did not accept the request. Nothing was created; it is safe to retry.",
                      "code": "upstream_unavailable"
                    }
                  }
                }
              }
            }
          },
          "429": {
            "description": "`rate_limited`: too many requests with an invalid API key from your IP address. Wait the number of seconds in `Retry-After`. Requests with a valid key are never throttled this way.",
            "content": {
              "application/problem+json": {
                "examples": {
                  "rate_limited": {
                    "summary": "rate_limited",
                    "value": {
                      "type": "https://partners.cbgc.io/docs/errors.md#rate_limited",
                      "title": "Too many requests",
                      "status": 429,
                      "detail": "Too many requests with an invalid API key from your IP address. Wait for the number of seconds in the Retry-After header.",
                      "code": "rate_limited"
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/clients/{clientRef}/products": {
      "get": {
        "tags": [
          "Products"
        ],
        "summary": "List a client's product levels",
        "description": "Returns the product levels this client can order, priced for this client (fees can differ per client).\nLevels the client doesn't have are left out.",
        "operationId": "ListClientProducts",
        "parameters": [
          {
            "name": "clientRef",
            "in": "path",
            "description": "The client: our `clientId` (`cl_…`) or your own `externalClientId` (at most 100 characters; URL-encode it). Both resolve only among your own clients; anything else is `404 client_not_found`.",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The levels this client can order, priced for it. Levels it doesn't have are left out.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/ProductResponse"
                  }
                }
              }
            }
          },
          "401": {
            "description": "`invalid_api_key`: the API key is missing, invalid or revoked, or it's a test key on production (or the reverse). Don't retry until the key is fixed.",
            "content": {
              "application/problem+json": {
                "examples": {
                  "invalid_api_key": {
                    "summary": "invalid_api_key",
                    "value": {
                      "type": "https://partners.cbgc.io/docs/errors.md#invalid_api_key",
                      "title": "Invalid API key",
                      "status": 401,
                      "detail": "The API key is missing, invalid or revoked.",
                      "code": "invalid_api_key"
                    }
                  }
                }
              }
            }
          },
          "404": {
            "description": "`client_not_found`: no client with that `clientId` or `externalClientId` exists under your account. Don't retry.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "client_not_found": {
                    "summary": "client_not_found",
                    "value": {
                      "type": "https://partners.cbgc.io/docs/errors.md#client_not_found",
                      "title": "Client not found",
                      "status": 404,
                      "detail": "No client 'org-1042' exists under your account.",
                      "code": "client_not_found"
                    }
                  }
                }
              }
            }
          },
          "502": {
            "description": "`upstream_unavailable`: the screening provider didn't answer. Safe to retry.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "upstream_unavailable": {
                    "summary": "upstream_unavailable",
                    "value": {
                      "type": "https://partners.cbgc.io/docs/errors.md#upstream_unavailable",
                      "title": "Screening provider unavailable",
                      "status": 502,
                      "detail": "The screening provider did not accept the request. Nothing was created; it is safe to retry.",
                      "code": "upstream_unavailable"
                    }
                  }
                }
              }
            }
          },
          "429": {
            "description": "`rate_limited`: too many requests with an invalid API key from your IP address. Wait the number of seconds in `Retry-After`. Requests with a valid key are never throttled this way.",
            "content": {
              "application/problem+json": {
                "examples": {
                  "rate_limited": {
                    "summary": "rate_limited",
                    "value": {
                      "type": "https://partners.cbgc.io/docs/errors.md#rate_limited",
                      "title": "Too many requests",
                      "status": 429,
                      "detail": "Too many requests with an invalid API key from your IP address. Wait for the number of seconds in the Retry-After header.",
                      "code": "rate_limited"
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/clients/{clientRef}/checks/batch": {
      "post": {
        "tags": [
          "Checks"
        ],
        "summary": "Order checks for a list of people",
        "description": "Orders checks for up to 500 people in one call. Send the `level`, an optional `orderedBy`, an optional\n`batchReference`, and the `applicants`.\n\nEvery applicant is validated and duplicate-checked first, against your stored checks and against the\napplicants before them in the same request. Each gets a `result`: `queued`, `duplicate` (with the\nexisting `check`) or `invalid` (with `errors`). A problem with one applicant never fails the batch;\nonly a problem with the batch itself (no applicants, more than 500, or a missing or invalid `level`) is a\n`400`.\n\nThe response is `202 Accepted` with a `batchId`. Queued checks are submitted in the background, a few at\na time; each sends its own `check.updated` webhook as it moves from `QUEUED` to `PENDING` to `CREATED`,\nand `batch.completed` is sent once none is waiting. Poll `GET /batches/{batchId}` if you prefer.\n\nSend a `batchReference` to make the call safe to retry: sending the same value again returns the first\nbatch with `200 OK` and orders nothing new. A client can have one running batch at a time (`409`).\n\nAn applicant whose `externalIdentifier` belongs to a check of another of your clients comes back as an `invalid`\nitem (`errors.externalIdentifier`); nothing is ordered for it.",
        "operationId": "OrderBatch",
        "parameters": [
          {
            "name": "clientRef",
            "in": "path",
            "description": "The client: our `clientId` (`cl_…`) or your own `externalClientId` (at most 100 characters; URL-encode it). Both resolve only among your own clients; anything else is `404 client_not_found`.",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/BatchOrderRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "202": {
            "description": "A new batch was accepted. Queued checks are submitted in the background; see each item's `result`. Billable per submitted check.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/BatchResponse"
                }
              }
            }
          },
          "200": {
            "description": "A batch with this `batchReference` already exists: this is that first batch, and nothing new was ordered.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/BatchResponse"
                }
              }
            }
          },
          "400": {
            "description": "`validation_failed`: a problem with the batch itself: no applicants, more than 500, a missing or invalid `level`, or a field too long. Problems with single applicants never fail the batch; they come back as `invalid` items.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/HttpValidationProblemDetails"
                },
                "examples": {
                  "validation_failed": {
                    "summary": "validation_failed",
                    "value": {
                      "type": "https://partners.cbgc.io/docs/errors.md#validation_failed",
                      "title": "One or more validation errors occurred.",
                      "status": 400,
                      "errors": {
                        "applicants": [
                          "applicants can hold at most 500 applicants per batch."
                        ]
                      },
                      "code": "validation_failed"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "`invalid_api_key`: the API key is missing, invalid or revoked, or it's a test key on production (or the reverse). Don't retry until the key is fixed.",
            "content": {
              "application/problem+json": {
                "examples": {
                  "invalid_api_key": {
                    "summary": "invalid_api_key",
                    "value": {
                      "type": "https://partners.cbgc.io/docs/errors.md#invalid_api_key",
                      "title": "Invalid API key",
                      "status": 401,
                      "detail": "The API key is missing, invalid or revoked.",
                      "code": "invalid_api_key"
                    }
                  }
                }
              }
            }
          },
          "404": {
            "description": "`client_not_found`: no client with that `clientId` or `externalClientId` exists under your account. Don't retry.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "client_not_found": {
                    "summary": "client_not_found",
                    "value": {
                      "type": "https://partners.cbgc.io/docs/errors.md#client_not_found",
                      "title": "Client not found",
                      "status": 404,
                      "detail": "No client 'org-1042' exists under your account.",
                      "code": "client_not_found"
                    }
                  }
                }
              }
            }
          },
          "409": {
            "description": "`batch_in_progress`: this client already has a batch that is still running. Nothing was ordered. Send the batch again once `GET /batches/{batchId}` shows the other one `completed`.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "batch_in_progress": {
                    "summary": "batch_in_progress",
                    "value": {
                      "type": "https://partners.cbgc.io/docs/errors.md#batch_in_progress",
                      "title": "Batch in progress",
                      "status": 409,
                      "detail": "This client already has a batch that is still running. Wait for it to complete (GET /batches/{batchId}) before sending another.",
                      "code": "batch_in_progress"
                    }
                  }
                }
              }
            }
          },
          "422": {
            "description": "`level_not_available`: this client has no product for that level. Nothing was ordered; contact support.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "level_not_available": {
                    "summary": "level_not_available",
                    "value": {
                      "type": "https://partners.cbgc.io/docs/errors.md#level_not_available",
                      "title": "Level not available",
                      "status": 422,
                      "detail": "This client has no product for level 4. Contact support.",
                      "code": "level_not_available"
                    }
                  }
                }
              }
            }
          },
          "502": {
            "description": "`upstream_unavailable`: the screening provider didn't answer. This happens before anything is stored (looking up the level's product), so nothing was ordered and a retry is safe.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "upstream_unavailable": {
                    "summary": "upstream_unavailable",
                    "value": {
                      "type": "https://partners.cbgc.io/docs/errors.md#upstream_unavailable",
                      "title": "Screening provider unavailable",
                      "status": 502,
                      "detail": "The screening provider did not accept the request. Nothing was created; it is safe to retry.",
                      "code": "upstream_unavailable"
                    }
                  }
                }
              }
            }
          },
          "429": {
            "description": "`rate_limited`: too many requests with an invalid API key from your IP address. Wait the number of seconds in `Retry-After`. Requests with a valid key are never throttled this way.",
            "content": {
              "application/problem+json": {
                "examples": {
                  "rate_limited": {
                    "summary": "rate_limited",
                    "value": {
                      "type": "https://partners.cbgc.io/docs/errors.md#rate_limited",
                      "title": "Too many requests",
                      "status": 429,
                      "detail": "Too many requests with an invalid API key from your IP address. Wait for the number of seconds in the Retry-After header.",
                      "code": "rate_limited"
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/batches/{batchId}": {
      "get": {
        "tags": [
          "Checks"
        ],
        "summary": "Get a batch",
        "description": "Returns a batch: counts of its checks by status, and one item per applicant of the original request.\n`status` is `processing` while any check is `QUEUED` or `PENDING`, and `completed` once none is.",
        "operationId": "GetBatch",
        "parameters": [
          {
            "name": "batchId",
            "in": "path",
            "description": "The batch's `batchId` (a UUID), from `POST /clients/{clientRef}/checks/batch`. Only your own batches resolve.",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The batch: counts by status and one item per applicant. `status` is `completed` once no check is `QUEUED` or `PENDING`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/BatchResponse"
                }
              }
            }
          },
          "401": {
            "description": "`invalid_api_key`: the API key is missing, invalid or revoked, or it's a test key on production (or the reverse). Don't retry until the key is fixed.",
            "content": {
              "application/problem+json": {
                "examples": {
                  "invalid_api_key": {
                    "summary": "invalid_api_key",
                    "value": {
                      "type": "https://partners.cbgc.io/docs/errors.md#invalid_api_key",
                      "title": "Invalid API key",
                      "status": 401,
                      "detail": "The API key is missing, invalid or revoked.",
                      "code": "invalid_api_key"
                    }
                  }
                }
              }
            }
          },
          "404": {
            "description": "`batch_not_found`: no batch with that id exists under your account.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "batch_not_found": {
                    "summary": "batch_not_found",
                    "value": {
                      "type": "https://partners.cbgc.io/docs/errors.md#batch_not_found",
                      "title": "Batch not found",
                      "status": 404,
                      "detail": "No batch '3f1c0b9e-7a52-4a6e-9d0c-5e7d2b1a8c44' exists under your account.",
                      "code": "batch_not_found"
                    }
                  }
                }
              }
            }
          },
          "429": {
            "description": "`rate_limited`: too many requests with an invalid API key from your IP address. Wait the number of seconds in `Retry-After`. Requests with a valid key are never throttled this way.",
            "content": {
              "application/problem+json": {
                "examples": {
                  "rate_limited": {
                    "summary": "rate_limited",
                    "value": {
                      "type": "https://partners.cbgc.io/docs/errors.md#rate_limited",
                      "title": "Too many requests",
                      "status": 429,
                      "detail": "Too many requests with an invalid API key from your IP address. Wait for the number of seconds in the Retry-After header.",
                      "code": "rate_limited"
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/status": {
      "get": {
        "tags": [
          "Account"
        ],
        "summary": "Check your API key",
        "description": "Returns `200` with a short message when the API key is valid. Useful for verifying setup and key rotation.",
        "operationId": "CheckApiKey",
        "responses": {
          "200": {
            "description": "The key is valid. The body is plain text.",
            "content": {
              "text/plain": {
                "schema": {
                  "type": "string"
                }
              }
            }
          },
          "401": {
            "description": "`invalid_api_key`: the API key is missing, invalid or revoked, or it's a test key on production (or the reverse). Don't retry until the key is fixed.",
            "content": {
              "application/problem+json": {
                "examples": {
                  "invalid_api_key": {
                    "summary": "invalid_api_key",
                    "value": {
                      "type": "https://partners.cbgc.io/docs/errors.md#invalid_api_key",
                      "title": "Invalid API key",
                      "status": 401,
                      "detail": "The API key is missing, invalid or revoked.",
                      "code": "invalid_api_key"
                    }
                  }
                }
              }
            }
          },
          "429": {
            "description": "`rate_limited`: too many requests with an invalid API key from your IP address. Wait the number of seconds in `Retry-After`. Requests with a valid key are never throttled this way.",
            "content": {
              "application/problem+json": {
                "examples": {
                  "rate_limited": {
                    "summary": "rate_limited",
                    "value": {
                      "type": "https://partners.cbgc.io/docs/errors.md#rate_limited",
                      "title": "Too many requests",
                      "status": 429,
                      "detail": "Too many requests with an invalid API key from your IP address. Wait for the number of seconds in the Retry-After header.",
                      "code": "rate_limited"
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/settings": {
      "get": {
        "tags": [
          "Settings"
        ],
        "summary": "Get your settings",
        "description": "Returns the webhook URL and the connect return URLs you registered, and how many years a completed check stays valid.",
        "operationId": "GetSettings",
        "responses": {
          "200": {
            "description": "Your registered webhook URL and connect return URLs.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SettingsResponse"
                }
              }
            }
          },
          "401": {
            "description": "`invalid_api_key`: the API key is missing, invalid or revoked, or it's a test key on production (or the reverse). Don't retry until the key is fixed.",
            "content": {
              "application/problem+json": {
                "examples": {
                  "invalid_api_key": {
                    "summary": "invalid_api_key",
                    "value": {
                      "type": "https://partners.cbgc.io/docs/errors.md#invalid_api_key",
                      "title": "Invalid API key",
                      "status": 401,
                      "detail": "The API key is missing, invalid or revoked.",
                      "code": "invalid_api_key"
                    }
                  }
                }
              }
            }
          },
          "429": {
            "description": "`rate_limited`: too many requests with an invalid API key from your IP address. Wait the number of seconds in `Retry-After`. Requests with a valid key are never throttled this way.",
            "content": {
              "application/problem+json": {
                "examples": {
                  "rate_limited": {
                    "summary": "rate_limited",
                    "value": {
                      "type": "https://partners.cbgc.io/docs/errors.md#rate_limited",
                      "title": "Too many requests",
                      "status": 429,
                      "detail": "Too many requests with an invalid API key from your IP address. Wait for the number of seconds in the Retry-After header.",
                      "code": "rate_limited"
                    }
                  }
                }
              }
            }
          }
        }
      },
      "put": {
        "tags": [
          "Settings"
        ],
        "summary": "Update your settings",
        "description": "Registers where we send webhooks and which return URLs the connect page may send a browser back to.\n\n`webhookUrl` must be an https:// URL whose host resolves only to public addresses; it is checked again\nwhenever a webhook is sent. Send an empty `webhookUrl` to stop webhooks (poll instead).\n`connectReturnUrls` holds up to 10 https:// URLs. A `returnUrl` in `POST /connect/sessions` must match one\nof them exactly. This call replaces both values; send the complete set.\n\n`checkValidityYears` (1 to 5, default 2) is how long a `CLEAR` or `REVIEW` check stays valid: its\n`expirationDate` is its completion plus that many years, and until then ordering the same applicant\nagain needs `forceReorder`. Omit it to keep the current value. A change applies to checks completed\nafter it.",
        "operationId": "UpdateSettings",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/SettingsRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "The saved settings. Both values were replaced with what you sent.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SettingsResponse"
                }
              }
            }
          },
          "400": {
            "description": "`validation_failed`: a URL isn't `https://`, has credentials or a fragment, or its host resolves to a private address, or there are more than 10 return URLs. Nothing was changed.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/HttpValidationProblemDetails"
                },
                "examples": {
                  "validation_failed": {
                    "summary": "validation_failed",
                    "value": {
                      "type": "https://partners.cbgc.io/docs/errors.md#validation_failed",
                      "title": "One or more validation errors occurred.",
                      "status": 400,
                      "errors": {
                        "webhookUrl": [
                          "webhookUrl must use https://."
                        ]
                      },
                      "code": "validation_failed"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "`invalid_api_key`: the API key is missing, invalid or revoked, or it's a test key on production (or the reverse). Don't retry until the key is fixed.",
            "content": {
              "application/problem+json": {
                "examples": {
                  "invalid_api_key": {
                    "summary": "invalid_api_key",
                    "value": {
                      "type": "https://partners.cbgc.io/docs/errors.md#invalid_api_key",
                      "title": "Invalid API key",
                      "status": 401,
                      "detail": "The API key is missing, invalid or revoked.",
                      "code": "invalid_api_key"
                    }
                  }
                }
              }
            }
          },
          "429": {
            "description": "`rate_limited`: too many requests with an invalid API key from your IP address. Wait the number of seconds in `Retry-After`. Requests with a valid key are never throttled this way.",
            "content": {
              "application/problem+json": {
                "examples": {
                  "rate_limited": {
                    "summary": "rate_limited",
                    "value": {
                      "type": "https://partners.cbgc.io/docs/errors.md#rate_limited",
                      "title": "Too many requests",
                      "status": 429,
                      "detail": "Too many requests with an invalid API key from your IP address. Wait for the number of seconds in the Retry-After header.",
                      "code": "rate_limited"
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/settings/webhook-secret": {
      "post": {
        "tags": [
          "Settings"
        ],
        "summary": "Rotate your webhook secret",
        "description": "Creates a new webhook signing secret and returns it. **It is shown only once**; store it before switching\nyour signature check over. The old secret stops working immediately, so webhooks sent before you switch\nwill fail verification until you do.",
        "operationId": "RotateWebhookSecret",
        "responses": {
          "200": {
            "description": "The new secret, shown only this once. The old one stopped working immediately.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PartnerWebhookSecretResponse"
                }
              }
            }
          },
          "401": {
            "description": "`invalid_api_key`: the API key is missing, invalid or revoked, or it's a test key on production (or the reverse). Don't retry until the key is fixed.",
            "content": {
              "application/problem+json": {
                "examples": {
                  "invalid_api_key": {
                    "summary": "invalid_api_key",
                    "value": {
                      "type": "https://partners.cbgc.io/docs/errors.md#invalid_api_key",
                      "title": "Invalid API key",
                      "status": 401,
                      "detail": "The API key is missing, invalid or revoked.",
                      "code": "invalid_api_key"
                    }
                  }
                }
              }
            }
          },
          "429": {
            "description": "`rate_limited`: too many requests with an invalid API key from your IP address. Wait the number of seconds in `Retry-After`. Requests with a valid key are never throttled this way.",
            "content": {
              "application/problem+json": {
                "examples": {
                  "rate_limited": {
                    "summary": "rate_limited",
                    "value": {
                      "type": "https://partners.cbgc.io/docs/errors.md#rate_limited",
                      "title": "Too many requests",
                      "status": 429,
                      "detail": "Too many requests with an invalid API key from your IP address. Wait for the number of seconds in the Retry-After header.",
                      "code": "rate_limited"
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/settings/webhook-test": {
      "post": {
        "tags": [
          "Settings"
        ],
        "summary": "Send a test webhook",
        "description": "Sends a signed `ping` event to your webhook URL, so you can verify your endpoint and your signature check.\nDelivery is asynchronous and retried like any other webhook.",
        "operationId": "TestWebhook",
        "responses": {
          "202": {
            "description": "A signed `ping` was queued for delivery to your webhook URL.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WebhookTestResponse"
                }
              }
            }
          },
          "400": {
            "description": "`validation_failed`: no webhook URL is registered. Register one with `PUT /settings` first.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/HttpValidationProblemDetails"
                },
                "examples": {
                  "validation_failed": {
                    "summary": "validation_failed",
                    "value": {
                      "type": "https://partners.cbgc.io/docs/errors.md#validation_failed",
                      "title": "One or more validation errors occurred.",
                      "status": 400,
                      "errors": {
                        "webhookUrl": [
                          "Register a webhookUrl with PUT /settings first."
                        ]
                      },
                      "code": "validation_failed"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "`invalid_api_key`: the API key is missing, invalid or revoked, or it's a test key on production (or the reverse). Don't retry until the key is fixed.",
            "content": {
              "application/problem+json": {
                "examples": {
                  "invalid_api_key": {
                    "summary": "invalid_api_key",
                    "value": {
                      "type": "https://partners.cbgc.io/docs/errors.md#invalid_api_key",
                      "title": "Invalid API key",
                      "status": 401,
                      "detail": "The API key is missing, invalid or revoked.",
                      "code": "invalid_api_key"
                    }
                  }
                }
              }
            }
          },
          "429": {
            "description": "`rate_limited`: too many requests with an invalid API key from your IP address. Wait the number of seconds in `Retry-After`. Requests with a valid key are never throttled this way.",
            "content": {
              "application/problem+json": {
                "examples": {
                  "rate_limited": {
                    "summary": "rate_limited",
                    "value": {
                      "type": "https://partners.cbgc.io/docs/errors.md#rate_limited",
                      "title": "Too many requests",
                      "status": 429,
                      "detail": "Too many requests with an invalid API key from your IP address. Wait for the number of seconds in the Retry-After header.",
                      "code": "rate_limited"
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/settings/webhook-deliveries": {
      "get": {
        "tags": [
          "Settings"
        ],
        "summary": "List recent webhook deliveries",
        "description": "Every attempt to deliver a webhook to your endpoint in the last 30 days, newest first: the event, its\n`eventId`, the check (for check events), your endpoint's HTTP status and how long it took. Use it to see\nwhat was sent and how your endpoint answered. Filter with `eventId` or `checkId`; `limit` is 1 to 200\n(default 50). Bodies are never stored, ours or yours: re-send a check's current state with\n`POST /clients/{clientRef}/checks/{checkRef}/replay-webhook`.",
        "operationId": "ListWebhookDeliveries",
        "parameters": [
          {
            "name": "limit",
            "in": "query",
            "description": "Page size, 1 to 500 (larger values are clamped). Defaults to 100.",
            "schema": {
              "pattern": "^-?(?:0|[1-9]\\d*)$",
              "type": [
                "integer",
                "string"
              ],
              "format": "int32"
            }
          },
          {
            "name": "eventId",
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "checkId",
            "in": "query",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/WebhookDeliveryResponse"
                  }
                }
              }
            }
          },
          "400": {
            "description": "Bad Request",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/HttpValidationProblemDetails"
                }
              }
            }
          },
          "401": {
            "description": "`invalid_api_key`: the API key is missing, invalid or revoked, or it's a test key on production (or the reverse). Don't retry until the key is fixed.",
            "content": {
              "application/problem+json": {
                "examples": {
                  "invalid_api_key": {
                    "summary": "invalid_api_key",
                    "value": {
                      "type": "https://partners.cbgc.io/docs/errors.md#invalid_api_key",
                      "title": "Invalid API key",
                      "status": 401,
                      "detail": "The API key is missing, invalid or revoked.",
                      "code": "invalid_api_key"
                    }
                  }
                }
              }
            }
          },
          "429": {
            "description": "`rate_limited`: too many requests with an invalid API key from your IP address. Wait the number of seconds in `Retry-After`. Requests with a valid key are never throttled this way.",
            "content": {
              "application/problem+json": {
                "examples": {
                  "rate_limited": {
                    "summary": "rate_limited",
                    "value": {
                      "type": "https://partners.cbgc.io/docs/errors.md#rate_limited",
                      "title": "Too many requests",
                      "status": 429,
                      "detail": "Too many requests with an invalid API key from your IP address. Wait for the number of seconds in the Retry-After header.",
                      "code": "rate_limited"
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/connect/sessions": {
      "post": {
        "tags": [
          "Connect"
        ],
        "summary": "Create a connect session",
        "description": "Starts the hosted connect page for one church. Call this from your server when the church's admin clicks\nyour \"Connect\" button, then send their browser to the returned `connectUrl` (a normal top-level\nnavigation; no iframe). The link works once and must be opened within 15 minutes.\n\nThe church enters its details and billing (card or invoice) on our page, so you never see card data or\npasswords. When it finishes we send the browser back to your `returnUrl` with `session_id` and `state`\n(`completed`, `cancelled` or `migration_requested`) and no secrets, and send a `client.connected` webhook.\n**Confirm with the webhook or `GET /connect/sessions/{sessionId}` before treating the church as connected.**\n\n`returnUrl` must exactly match one of your registered `connectReturnUrls` (`PUT /settings`).\n`409` if `externalClientId` is already an active client.\n\nThe church's primary contact gets an email from Datasource with their sign-in and a temporary password (changed at first\nsign-in). Submitting the page twice is safe: the second submit is told the account is being created, and only one\naccount is ever made. Invoice billing uses the billing setup copied from your account plus the church's billing\ncontact; a card is entered by the church on our page and never reaches you.",
        "operationId": "CreateConnectSession",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ConnectSessionRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "201": {
            "description": "The session was created. Send the church's browser to `connectUrl` within 15 minutes; the link works once.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ConnectSessionResponse"
                }
              }
            }
          },
          "400": {
            "description": "`validation_failed` (a field is missing or too long) or `invalid_return_url` (`returnUrl` isn't one of your registered `connectReturnUrls`; register it with `PUT /settings`). Nothing was created.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/HttpValidationProblemDetails"
                },
                "examples": {
                  "invalid_return_url": {
                    "summary": "invalid_return_url",
                    "value": {
                      "type": "https://partners.cbgc.io/docs/errors.md#invalid_return_url",
                      "title": "Return URL not registered",
                      "status": 400,
                      "detail": "returnUrl must exactly match one of your registered connectReturnUrls. Register it with PUT /settings first.",
                      "code": "invalid_return_url"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "`invalid_api_key`: the API key is missing, invalid or revoked, or it's a test key on production (or the reverse). Don't retry until the key is fixed.",
            "content": {
              "application/problem+json": {
                "examples": {
                  "invalid_api_key": {
                    "summary": "invalid_api_key",
                    "value": {
                      "type": "https://partners.cbgc.io/docs/errors.md#invalid_api_key",
                      "title": "Invalid API key",
                      "status": 401,
                      "detail": "The API key is missing, invalid or revoked.",
                      "code": "invalid_api_key"
                    }
                  }
                }
              }
            }
          },
          "409": {
            "description": "`client_already_connected`: this `externalClientId` is already an active client. Use it (`GET /clients?externalClientId=`) instead of connecting again.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "client_already_connected": {
                    "summary": "client_already_connected",
                    "value": {
                      "type": "https://partners.cbgc.io/docs/errors.md#client_already_connected",
                      "title": "Client already connected",
                      "status": 409,
                      "detail": "A client with externalClientId 'org-1042' is already active under your account.",
                      "code": "client_already_connected"
                    }
                  }
                }
              }
            }
          },
          "429": {
            "description": "`rate_limited`: more than 30 sessions were created in a minute (or too many invalid-key requests came from your IP address). Wait `Retry-After` seconds (60).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "rate_limited": {
                    "summary": "rate_limited",
                    "value": {
                      "type": "https://partners.cbgc.io/docs/errors.md#rate_limited",
                      "title": "Too many requests",
                      "status": 429,
                      "detail": "At most 30 connect sessions can be created per minute. Wait a minute and try again.",
                      "code": "rate_limited"
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/connect/sessions/{sessionId}": {
      "get": {
        "tags": [
          "Connect"
        ],
        "summary": "Get a connect session",
        "description": "Returns where a connect session stands: `created`, `opened`, `completed` (with the new `clientId`),\n`cancelled`, `expired` or `migration_requested`. Use it, or the `client.connected` webhook, to confirm a\nchurch is connected after the browser returns to you.",
        "operationId": "GetConnectSession",
        "parameters": [
          {
            "name": "sessionId",
            "in": "path",
            "description": "The session's `sessionId` (a UUID), from `POST /connect/sessions` or the `session_id` on the browser's return. Only your own sessions resolve.",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Where the session stands. `clientId` is set once `state` is `completed`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ConnectSessionStatusResponse"
                }
              }
            }
          },
          "401": {
            "description": "`invalid_api_key`: the API key is missing, invalid or revoked, or it's a test key on production (or the reverse). Don't retry until the key is fixed.",
            "content": {
              "application/problem+json": {
                "examples": {
                  "invalid_api_key": {
                    "summary": "invalid_api_key",
                    "value": {
                      "type": "https://partners.cbgc.io/docs/errors.md#invalid_api_key",
                      "title": "Invalid API key",
                      "status": 401,
                      "detail": "The API key is missing, invalid or revoked.",
                      "code": "invalid_api_key"
                    }
                  }
                }
              }
            }
          },
          "404": {
            "description": "`session_not_found`: no session with that id exists under your account.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "session_not_found": {
                    "summary": "session_not_found",
                    "value": {
                      "type": "https://partners.cbgc.io/docs/errors.md#session_not_found",
                      "title": "Connect session not found",
                      "status": 404,
                      "detail": "No connect session '8d2f6c14-3b7a-4e0e-9c55-1a9b7d3e2f60' exists under your account.",
                      "code": "session_not_found"
                    }
                  }
                }
              }
            }
          },
          "429": {
            "description": "`rate_limited`: too many requests with an invalid API key from your IP address. Wait the number of seconds in `Retry-After`. Requests with a valid key are never throttled this way.",
            "content": {
              "application/problem+json": {
                "examples": {
                  "rate_limited": {
                    "summary": "rate_limited",
                    "value": {
                      "type": "https://partners.cbgc.io/docs/errors.md#rate_limited",
                      "title": "Too many requests",
                      "status": 429,
                      "detail": "Too many requests with an invalid API key from your IP address. Wait for the number of seconds in the Retry-After header.",
                      "code": "rate_limited"
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/clients/{clientRef}/checks": {
      "post": {
        "tags": [
          "Checks"
        ],
        "summary": "Order a background check",
        "description": "Orders a background check for an applicant. The applicant receives an email invitation to provide their\npersonal details and consent directly with us, so you only send their name, email and what to order.\n\n**What to order.** Send the `level` (1 to 6, see `GET /products`). We translate\nthe level to the client's own product; `422 Level not available` means the client doesn't have that level\n(contact support). `orderedBy` is optional: it is written to the order's notes so the client's staff can\nsee who asked, and is never stored by us or used to create a login.\n\n**Job location.** Your CHMS must collect each applicant's job location and state of employment from the\nchurch and send them as `jobLocation` and `stateOfEmployment`. They decide which state's rules apply, and\nsome levels refuse an order without them. Send only what the church tells you; never use the church's own\naddress or another guess.\n\n**Duplicate protection.** An order is rejected with `409 Conflict` (and nothing is billed) when the same\n`externalIdentifier`, or the same applicant email for this client, already has a check that is in progress\n(`PENDING`, `CREATED`) or completed and not yet expired (`CLEAR`, `REVIEW`). Checks that ended in `ERROR` or\n`CANCELLED`, or have expired, can be re-ordered normally. Set `forceReorder` to order anyway; that is billed\nas a new check.\n\n**Retries.** Always send an `externalIdentifier`, and reuse it when retrying after a timeout or network\nerror: a retry can never create a second check.\n\n**502 responses.** If submission to the screening provider fails, the check is kept as `PENDING` and returned\nin the body. Don't retry with `forceReorder`; the check will update via webhook or polling, or contact support\nwith its `id`.\n\n**One `externalIdentifier`, one check, across all your clients.** An `externalIdentifier` already used by a check\nof another of your clients is `409 external_identifier_in_use`; nothing is ordered. Reusing it for the same client\nis the normal retry and re-order path.\n\n**`PENDING` checks resolve on their own.** A check left `PENDING` by a lost answer is looked up with the screening\nprovider every 10 minutes: if the provider has it, it takes the provider's status; if the provider still doesn't\nhave it after an hour, it becomes `ERROR` (never submitted, so ordering again is allowed). Either way you get\n`check.updated`.\n\n**Statuses.** `CLEAR` is reported only when the provider says no search found records. Any other completed\nresult is `REVIEW` (flagged), so your users look at it in the portal.",
        "operationId": "OrderCheck",
        "parameters": [
          {
            "name": "clientRef",
            "in": "path",
            "description": "The client: our `clientId` (`cl_…`) or your own `externalClientId` (at most 100 characters; URL-encode it). Both resolve only among your own clients; anything else is `404 client_not_found`.",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreateOrderRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "201": {
            "description": "The check was submitted (`status` `CREATED`) and the applicant has been emailed. Billable. `Location` points at the check.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CheckResponse"
                }
              }
            }
          },
          "400": {
            "description": "`validation_failed`: a field is missing or invalid (for example no `level`). Nothing was ordered.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/HttpValidationProblemDetails"
                },
                "examples": {
                  "validation_failed": {
                    "summary": "validation_failed",
                    "value": {
                      "type": "https://partners.cbgc.io/docs/errors.md#validation_failed",
                      "title": "One or more validation errors occurred.",
                      "status": 400,
                      "errors": {
                        "level": [
                          "level is required."
                        ]
                      },
                      "code": "validation_failed"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "`invalid_api_key`: the API key is missing, invalid or revoked, or it's a test key on production (or the reverse). Don't retry until the key is fixed.",
            "content": {
              "application/problem+json": {
                "examples": {
                  "invalid_api_key": {
                    "summary": "invalid_api_key",
                    "value": {
                      "type": "https://partners.cbgc.io/docs/errors.md#invalid_api_key",
                      "title": "Invalid API key",
                      "status": 401,
                      "detail": "The API key is missing, invalid or revoked.",
                      "code": "invalid_api_key"
                    }
                  }
                }
              }
            }
          },
          "404": {
            "description": "`client_not_found`: no client with that `clientId` or `externalClientId` exists under your account. Don't retry.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "client_not_found": {
                    "summary": "client_not_found",
                    "value": {
                      "type": "https://partners.cbgc.io/docs/errors.md#client_not_found",
                      "title": "Client not found",
                      "status": 404,
                      "detail": "No client 'org-1042' exists under your account.",
                      "code": "client_not_found"
                    }
                  }
                }
              }
            }
          },
          "409": {
            "description": "Nothing was ordered or billed. Two cases, told apart by `code`: `duplicate_check` (this body: the applicant already has a check in progress or unexpired, in `order`), or `external_identifier_in_use` (problem details: the `externalIdentifier` belongs to a check of another of your clients; use a different one).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/OrderConflictResponse"
                },
                "examples": {
                  "duplicate_check": {
                    "summary": "duplicate_check",
                    "value": {
                      "code": "duplicate_check",
                      "message": "A background check is currently in progress with status 'CREATED'.",
                      "currentStatus": "CREATED",
                      "instructions": "To force a re-order, submit the request with 'forceReorder': true.",
                      "order": {
                        "id": "chk_9c2e4f6a1b3d4e5f8a7b6c5d",
                        "externalIdentifier": "vol-8841",
                        "clientId": "cl_5b0e6f2a8c1d4e7b9a3f2d6c",
                        "status": "CREATED",
                        "stage": "awaiting_applicant",
                        "level": 2,
                        "expirationDate": null,
                        "completedAt": null,
                        "createdAt": "2026-10-16T15:10:00Z",
                        "updatedAt": "2026-10-16T15:10:01Z"
                      }
                    }
                  }
                }
              }
            }
          },
          "422": {
            "description": "`level_not_available`: this client has no product for that level. A configuration problem on our side; contact support. Nothing was ordered.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "level_not_available": {
                    "summary": "level_not_available",
                    "value": {
                      "type": "https://partners.cbgc.io/docs/errors.md#level_not_available",
                      "title": "Level not available",
                      "status": 422,
                      "detail": "This client has no product for level 4. Contact support.",
                      "code": "level_not_available"
                    }
                  }
                }
              }
            }
          },
          "502": {
            "description": "Two cases. If the body is a check (`status` `PENDING`, no `code`), the submission's answer was lost: the check may exist upstream, is kept `PENDING`, and is resolved automatically within about an hour (`check.updated` follows). Retry only with the same `externalIdentifier`, never with `forceReorder`. If the body is problem details with `upstream_unavailable`, nothing was created or recorded, and a retry is safe.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CheckResponse"
                },
                "examples": {
                  "pending_check": {
                    "summary": "pending_check",
                    "value": {
                      "id": "chk_9c2e4f6a1b3d4e5f8a7b6c5d",
                      "externalIdentifier": "vol-8841",
                      "clientId": "cl_5b0e6f2a8c1d4e7b9a3f2d6c",
                      "status": "PENDING",
                      "stage": "submitting",
                      "level": 2,
                      "expirationDate": null,
                      "completedAt": null,
                      "createdAt": "2026-10-16T15:10:00Z",
                      "updatedAt": "2026-10-16T15:10:00Z"
                    }
                  }
                }
              }
            }
          },
          "429": {
            "description": "`rate_limited`: too many requests with an invalid API key from your IP address. Wait the number of seconds in `Retry-After`. Requests with a valid key are never throttled this way.",
            "content": {
              "application/problem+json": {
                "examples": {
                  "rate_limited": {
                    "summary": "rate_limited",
                    "value": {
                      "type": "https://partners.cbgc.io/docs/errors.md#rate_limited",
                      "title": "Too many requests",
                      "status": 429,
                      "detail": "Too many requests with an invalid API key from your IP address. Wait for the number of seconds in the Retry-After header.",
                      "code": "rate_limited"
                    }
                  }
                }
              }
            }
          }
        }
      },
      "get": {
        "tags": [
          "Checks"
        ],
        "summary": "List checks",
        "description": "Returns a client's checks, oldest change first, one page at a time. Use it for reconciliation, e.g. a\nnightly job that catches any webhook you missed.\n\nPass `updated_since` to get only checks that changed at or after that time, and follow `nextCursor` until it\nis null. A check that changes while you are paging may appear again on a later page, so process items\nidempotently (by `id`).",
        "operationId": "ListChecks",
        "parameters": [
          {
            "name": "clientRef",
            "in": "path",
            "description": "The client: our `clientId` (`cl_…`) or your own `externalClientId` (at most 100 characters; URL-encode it). Both resolve only among your own clients; anything else is `404 client_not_found`.",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "updated_since",
            "in": "query",
            "description": "Only return checks whose `updatedAt` is at or after this time. ISO 8601; a time with no offset is treated as UTC. Omit for all checks.",
            "schema": {
              "type": "string",
              "format": "date-time"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "description": "Page size, 1 to 500 (larger values are clamped). Defaults to 100.",
            "schema": {
              "pattern": "^-?(?:0|[1-9]\\d*)$",
              "type": [
                "integer",
                "string"
              ],
              "format": "int32"
            }
          },
          {
            "name": "cursor",
            "in": "query",
            "description": "The `nextCursor` from the previous page. Omit for the first page. An unknown value is `400 invalid_cursor`.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "One page of the client's checks, oldest change first. Follow `nextCursor` until it is null.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CheckPage"
                }
              }
            }
          },
          "400": {
            "description": "`invalid_cursor`: `cursor` isn't a `nextCursor` we issued; start again from the first page. (`invalid_json`: `updated_since` or `limit` isn't a valid value.)",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/HttpValidationProblemDetails"
                },
                "examples": {
                  "invalid_cursor": {
                    "summary": "invalid_cursor",
                    "value": {
                      "type": "https://partners.cbgc.io/docs/errors.md#invalid_cursor",
                      "title": "One or more validation errors occurred.",
                      "status": 400,
                      "errors": {
                        "cursor": [
                          "cursor is not valid. Use the nextCursor value from a previous page."
                        ]
                      },
                      "code": "invalid_cursor"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "`invalid_api_key`: the API key is missing, invalid or revoked, or it's a test key on production (or the reverse). Don't retry until the key is fixed.",
            "content": {
              "application/problem+json": {
                "examples": {
                  "invalid_api_key": {
                    "summary": "invalid_api_key",
                    "value": {
                      "type": "https://partners.cbgc.io/docs/errors.md#invalid_api_key",
                      "title": "Invalid API key",
                      "status": 401,
                      "detail": "The API key is missing, invalid or revoked.",
                      "code": "invalid_api_key"
                    }
                  }
                }
              }
            }
          },
          "404": {
            "description": "`client_not_found`: no client with that `clientId` or `externalClientId` exists under your account. Don't retry.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "client_not_found": {
                    "summary": "client_not_found",
                    "value": {
                      "type": "https://partners.cbgc.io/docs/errors.md#client_not_found",
                      "title": "Client not found",
                      "status": 404,
                      "detail": "No client 'org-1042' exists under your account.",
                      "code": "client_not_found"
                    }
                  }
                }
              }
            }
          },
          "429": {
            "description": "`rate_limited`: too many requests with an invalid API key from your IP address. Wait the number of seconds in `Retry-After`. Requests with a valid key are never throttled this way.",
            "content": {
              "application/problem+json": {
                "examples": {
                  "rate_limited": {
                    "summary": "rate_limited",
                    "value": {
                      "type": "https://partners.cbgc.io/docs/errors.md#rate_limited",
                      "title": "Too many requests",
                      "status": 429,
                      "detail": "Too many requests with an invalid API key from your IP address. Wait for the number of seconds in the Retry-After header.",
                      "code": "rate_limited"
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/clients/{clientRef}/checks/lookup": {
      "post": {
        "tags": [
          "Checks"
        ],
        "summary": "Find existing checks by email",
        "description": "Finds the latest check for each of up to 500 applicant emails, including history imported when a church was\nmoved to you. Each result is keyed by the email's position in your request (`index`): emails are never\nreturned. Use it to match your own people to existing checks, then link them with\n`PATCH /clients/{clientRef}/checks/{checkRef}`.",
        "operationId": "LookupChecks",
        "parameters": [
          {
            "name": "clientRef",
            "in": "path",
            "description": "The client: our `clientId` (`cl_…`) or your own `externalClientId` (at most 100 characters; URL-encode it). Both resolve only among your own clients; anything else is `404 client_not_found`.",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CheckLookupRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "One item per email, in request order, keyed by `index`. `check` is the latest check for that email at this client, or null. Emails are never returned.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CheckLookupResponse"
                }
              }
            }
          },
          "400": {
            "description": "`validation_failed`: `emails` is empty, has more than 500 entries, or an entry is longer than 254 characters.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/HttpValidationProblemDetails"
                },
                "examples": {
                  "validation_failed": {
                    "summary": "validation_failed",
                    "value": {
                      "type": "https://partners.cbgc.io/docs/errors.md#validation_failed",
                      "title": "One or more validation errors occurred.",
                      "status": 400,
                      "errors": {
                        "emails": [
                          "emails can hold at most 500 addresses."
                        ]
                      },
                      "code": "validation_failed"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "`invalid_api_key`: the API key is missing, invalid or revoked, or it's a test key on production (or the reverse). Don't retry until the key is fixed.",
            "content": {
              "application/problem+json": {
                "examples": {
                  "invalid_api_key": {
                    "summary": "invalid_api_key",
                    "value": {
                      "type": "https://partners.cbgc.io/docs/errors.md#invalid_api_key",
                      "title": "Invalid API key",
                      "status": 401,
                      "detail": "The API key is missing, invalid or revoked.",
                      "code": "invalid_api_key"
                    }
                  }
                }
              }
            }
          },
          "404": {
            "description": "`client_not_found`: no client with that `clientId` or `externalClientId` exists under your account. Don't retry.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "client_not_found": {
                    "summary": "client_not_found",
                    "value": {
                      "type": "https://partners.cbgc.io/docs/errors.md#client_not_found",
                      "title": "Client not found",
                      "status": 404,
                      "detail": "No client 'org-1042' exists under your account.",
                      "code": "client_not_found"
                    }
                  }
                }
              }
            }
          },
          "429": {
            "description": "`rate_limited`: too many requests with an invalid API key from your IP address. Wait the number of seconds in `Retry-After`. Requests with a valid key are never throttled this way.",
            "content": {
              "application/problem+json": {
                "examples": {
                  "rate_limited": {
                    "summary": "rate_limited",
                    "value": {
                      "type": "https://partners.cbgc.io/docs/errors.md#rate_limited",
                      "title": "Too many requests",
                      "status": 429,
                      "detail": "Too many requests with an invalid API key from your IP address. Wait for the number of seconds in the Retry-After header.",
                      "code": "rate_limited"
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/clients/{clientRef}/checks/{checkRef}": {
      "patch": {
        "tags": [
          "Checks"
        ],
        "summary": "Set your identifier on a check",
        "description": "Sets `externalIdentifier` on a check that doesn't have one yet, so you can link a check (for example one found\nwith `POST /clients/{clientRef}/checks/lookup`) to your own record. `409` if the check already has an\nidentifier, or if another of your checks uses that value. `checkRef` is the check's `id` (`chk_…`).",
        "operationId": "UpdateCheck",
        "parameters": [
          {
            "name": "clientRef",
            "in": "path",
            "description": "The client: our `clientId` (`cl_…`) or your own `externalClientId` (at most 100 characters; URL-encode it). Both resolve only among your own clients; anything else is `404 client_not_found`.",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "checkRef",
            "in": "path",
            "description": "The check: its `id` (`chk_…`) or your `externalIdentifier`. Resolves only among your own checks (of that client, where the path names one).",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/UpdateCheckRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "The check, now with your `externalIdentifier`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CheckResponse"
                }
              }
            }
          },
          "400": {
            "description": "`validation_failed`: `externalIdentifier` is missing or longer than 100 characters.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/HttpValidationProblemDetails"
                },
                "examples": {
                  "validation_failed": {
                    "summary": "validation_failed",
                    "value": {
                      "type": "https://partners.cbgc.io/docs/errors.md#validation_failed",
                      "title": "One or more validation errors occurred.",
                      "status": 400,
                      "errors": {
                        "externalIdentifier": [
                          "externalIdentifier is required."
                        ]
                      },
                      "code": "validation_failed"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "`invalid_api_key`: the API key is missing, invalid or revoked, or it's a test key on production (or the reverse). Don't retry until the key is fixed.",
            "content": {
              "application/problem+json": {
                "examples": {
                  "invalid_api_key": {
                    "summary": "invalid_api_key",
                    "value": {
                      "type": "https://partners.cbgc.io/docs/errors.md#invalid_api_key",
                      "title": "Invalid API key",
                      "status": 401,
                      "detail": "The API key is missing, invalid or revoked.",
                      "code": "invalid_api_key"
                    }
                  }
                }
              }
            }
          },
          "404": {
            "description": "`client_not_found` or `check_not_found`: the client, or the check within it, doesn't exist under your account. Don't retry.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "check_not_found": {
                    "summary": "check_not_found",
                    "value": {
                      "type": "https://partners.cbgc.io/docs/errors.md#check_not_found",
                      "title": "Check not found",
                      "status": 404,
                      "detail": "No check 'vol-8841' exists for client 'org-1042'.",
                      "code": "check_not_found"
                    }
                  }
                }
              }
            }
          },
          "409": {
            "description": "`external_identifier_in_use`: the check already has an `externalIdentifier` (it can be set once), or another of your checks uses that value. Nothing changed.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "external_identifier_in_use": {
                    "summary": "external_identifier_in_use",
                    "value": {
                      "type": "https://partners.cbgc.io/docs/errors.md#external_identifier_in_use",
                      "title": "External identifier in use",
                      "status": 409,
                      "detail": "Another of your checks already uses that externalIdentifier.",
                      "code": "external_identifier_in_use"
                    }
                  }
                }
              }
            }
          },
          "429": {
            "description": "`rate_limited`: too many requests with an invalid API key from your IP address. Wait the number of seconds in `Retry-After`. Requests with a valid key are never throttled this way.",
            "content": {
              "application/problem+json": {
                "examples": {
                  "rate_limited": {
                    "summary": "rate_limited",
                    "value": {
                      "type": "https://partners.cbgc.io/docs/errors.md#rate_limited",
                      "title": "Too many requests",
                      "status": 429,
                      "detail": "Too many requests with an invalid API key from your IP address. Wait for the number of seconds in the Retry-After header.",
                      "code": "rate_limited"
                    }
                  }
                }
              }
            }
          }
        }
      },
      "get": {
        "tags": [
          "Checks"
        ],
        "summary": "Get a check",
        "description": "Returns one check. `checkRef` may be the check's `id` (`chk_…`) or your `externalIdentifier`.",
        "operationId": "GetCheck",
        "parameters": [
          {
            "name": "clientRef",
            "in": "path",
            "description": "The client: our `clientId` (`cl_…`) or your own `externalClientId` (at most 100 characters; URL-encode it). Both resolve only among your own clients; anything else is `404 client_not_found`.",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "checkRef",
            "in": "path",
            "description": "The check: its `id` (`chk_…`) or your `externalIdentifier`. Resolves only among your own checks (of that client, where the path names one).",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The check's current state.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CheckResponse"
                }
              }
            }
          },
          "401": {
            "description": "`invalid_api_key`: the API key is missing, invalid or revoked, or it's a test key on production (or the reverse). Don't retry until the key is fixed.",
            "content": {
              "application/problem+json": {
                "examples": {
                  "invalid_api_key": {
                    "summary": "invalid_api_key",
                    "value": {
                      "type": "https://partners.cbgc.io/docs/errors.md#invalid_api_key",
                      "title": "Invalid API key",
                      "status": 401,
                      "detail": "The API key is missing, invalid or revoked.",
                      "code": "invalid_api_key"
                    }
                  }
                }
              }
            }
          },
          "404": {
            "description": "`client_not_found` or `check_not_found`: the client, or the check within it, doesn't exist under your account. Don't retry.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "check_not_found": {
                    "summary": "check_not_found",
                    "value": {
                      "type": "https://partners.cbgc.io/docs/errors.md#check_not_found",
                      "title": "Check not found",
                      "status": 404,
                      "detail": "No check 'vol-8841' exists for client 'org-1042'.",
                      "code": "check_not_found"
                    }
                  }
                }
              }
            }
          },
          "429": {
            "description": "`rate_limited`: too many requests with an invalid API key from your IP address. Wait the number of seconds in `Retry-After`. Requests with a valid key are never throttled this way.",
            "content": {
              "application/problem+json": {
                "examples": {
                  "rate_limited": {
                    "summary": "rate_limited",
                    "value": {
                      "type": "https://partners.cbgc.io/docs/errors.md#rate_limited",
                      "title": "Too many requests",
                      "status": 429,
                      "detail": "Too many requests with an invalid API key from your IP address. Wait for the number of seconds in the Retry-After header.",
                      "code": "rate_limited"
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/clients/{clientRef}/checks/{checkRef}/cancel": {
      "post": {
        "tags": [
          "Checks"
        ],
        "summary": "Cancel a check",
        "description": "Cancels a check, e.g. one ordered by mistake. Cancel before the applicant submits their information to avoid\ncharges. `checkRef` may be the check's `id` (`chk_…`) or your `externalIdentifier`.\n\nThe check is marked `CANCELLED` either way. `upstreamCancelled` tells you whether the screening provider\nconfirmed it; if it's false, the check may still be processed, so contact support.",
        "operationId": "CancelCheck",
        "parameters": [
          {
            "name": "clientRef",
            "in": "path",
            "description": "The client: our `clientId` (`cl_…`) or your own `externalClientId` (at most 100 characters; URL-encode it). Both resolve only among your own clients; anything else is `404 client_not_found`.",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "checkRef",
            "in": "path",
            "description": "The check: its `id` (`chk_…`) or your `externalIdentifier`. Resolves only among your own checks (of that client, where the path names one).",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The check is now `CANCELLED`. If `upstreamCancelled` is false the screening provider didn't confirm it and the check may still be processed and billed: contact support.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CancelCheckResponse"
                }
              }
            }
          },
          "401": {
            "description": "`invalid_api_key`: the API key is missing, invalid or revoked, or it's a test key on production (or the reverse). Don't retry until the key is fixed.",
            "content": {
              "application/problem+json": {
                "examples": {
                  "invalid_api_key": {
                    "summary": "invalid_api_key",
                    "value": {
                      "type": "https://partners.cbgc.io/docs/errors.md#invalid_api_key",
                      "title": "Invalid API key",
                      "status": 401,
                      "detail": "The API key is missing, invalid or revoked.",
                      "code": "invalid_api_key"
                    }
                  }
                }
              }
            }
          },
          "404": {
            "description": "`client_not_found` or `check_not_found`: the client, or the check within it, doesn't exist under your account. Don't retry.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "check_not_found": {
                    "summary": "check_not_found",
                    "value": {
                      "type": "https://partners.cbgc.io/docs/errors.md#check_not_found",
                      "title": "Check not found",
                      "status": 404,
                      "detail": "No check 'vol-8841' exists for client 'org-1042'.",
                      "code": "check_not_found"
                    }
                  }
                }
              }
            }
          },
          "429": {
            "description": "`rate_limited`: too many requests with an invalid API key from your IP address. Wait the number of seconds in `Retry-After`. Requests with a valid key are never throttled this way.",
            "content": {
              "application/problem+json": {
                "examples": {
                  "rate_limited": {
                    "summary": "rate_limited",
                    "value": {
                      "type": "https://partners.cbgc.io/docs/errors.md#rate_limited",
                      "title": "Too many requests",
                      "status": 429,
                      "detail": "Too many requests with an invalid API key from your IP address. Wait for the number of seconds in the Retry-After header.",
                      "code": "rate_limited"
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/clients/{clientRef}/checks/{checkRef}/remind": {
      "post": {
        "tags": [
          "Checks"
        ],
        "summary": "Re-send the applicant's invitation",
        "description": "Sends the applicant their invitation again: by email, and by text if `applicantPhone` was sent with the\norder. Only for a check waiting for the applicant (`CREATED`, stage `awaiting_applicant`), at most once an\nhour. `checkRef` may be the check's `id` (`chk_…`) or your `externalIdentifier`.\n\n`409 check_not_awaiting_applicant` if the applicant already finished (or the check is closed);\n`409 reminder_too_soon` within an hour of the last reminder.",
        "operationId": "RemindApplicant",
        "parameters": [
          {
            "name": "clientRef",
            "in": "path",
            "description": "The client: our `clientId` (`cl_…`) or your own `externalClientId` (at most 100 characters; URL-encode it). Both resolve only among your own clients; anything else is `404 client_not_found`.",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "checkRef",
            "in": "path",
            "description": "The check: its `id` (`chk_…`) or your `externalIdentifier`. Resolves only among your own checks (of that client, where the path names one).",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RemindCheckResponse"
                }
              }
            }
          },
          "401": {
            "description": "`invalid_api_key`: the API key is missing, invalid or revoked, or it's a test key on production (or the reverse). Don't retry until the key is fixed.",
            "content": {
              "application/problem+json": {
                "examples": {
                  "invalid_api_key": {
                    "summary": "invalid_api_key",
                    "value": {
                      "type": "https://partners.cbgc.io/docs/errors.md#invalid_api_key",
                      "title": "Invalid API key",
                      "status": 401,
                      "detail": "The API key is missing, invalid or revoked.",
                      "code": "invalid_api_key"
                    }
                  }
                }
              }
            }
          },
          "404": {
            "description": "Not Found",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "409": {
            "description": "Conflict",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "502": {
            "description": "Bad Gateway",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "429": {
            "description": "`rate_limited`: too many requests with an invalid API key from your IP address. Wait the number of seconds in `Retry-After`. Requests with a valid key are never throttled this way.",
            "content": {
              "application/problem+json": {
                "examples": {
                  "rate_limited": {
                    "summary": "rate_limited",
                    "value": {
                      "type": "https://partners.cbgc.io/docs/errors.md#rate_limited",
                      "title": "Too many requests",
                      "status": 429,
                      "detail": "Too many requests with an invalid API key from your IP address. Wait for the number of seconds in the Retry-After header.",
                      "code": "rate_limited"
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/clients/{clientRef}/checks/{checkRef}/replay-webhook": {
      "post": {
        "tags": [
          "Checks"
        ],
        "summary": "Re-send a check's webhook",
        "description": "Queues a new delivery of the `check.updated` webhook with the check's current state, e.g. after your endpoint\nwas down. Delivery is asynchronous and retried like any other webhook. `checkRef` may be the check's `id` (`chk_…`) or your `externalIdentifier`.",
        "operationId": "ReplayWebhook",
        "parameters": [
          {
            "name": "clientRef",
            "in": "path",
            "description": "The client: our `clientId` (`cl_…`) or your own `externalClientId` (at most 100 characters; URL-encode it). Both resolve only among your own clients; anything else is `404 client_not_found`.",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "checkRef",
            "in": "path",
            "description": "The check: its `id` (`chk_…`) or your `externalIdentifier`. Resolves only among your own checks (of that client, where the path names one).",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "A new `check.updated` delivery with the check's current state was queued (a new `eventId`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ReplayWebhookResponse"
                }
              }
            }
          },
          "401": {
            "description": "`invalid_api_key`: the API key is missing, invalid or revoked, or it's a test key on production (or the reverse). Don't retry until the key is fixed.",
            "content": {
              "application/problem+json": {
                "examples": {
                  "invalid_api_key": {
                    "summary": "invalid_api_key",
                    "value": {
                      "type": "https://partners.cbgc.io/docs/errors.md#invalid_api_key",
                      "title": "Invalid API key",
                      "status": 401,
                      "detail": "The API key is missing, invalid or revoked.",
                      "code": "invalid_api_key"
                    }
                  }
                }
              }
            }
          },
          "404": {
            "description": "`client_not_found` or `check_not_found`: the client, or the check within it, doesn't exist under your account. Don't retry.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "examples": {
                  "check_not_found": {
                    "summary": "check_not_found",
                    "value": {
                      "type": "https://partners.cbgc.io/docs/errors.md#check_not_found",
                      "title": "Check not found",
                      "status": 404,
                      "detail": "No check 'vol-8841' exists for client 'org-1042'.",
                      "code": "check_not_found"
                    }
                  }
                }
              }
            }
          },
          "429": {
            "description": "`rate_limited`: too many requests with an invalid API key from your IP address. Wait the number of seconds in `Retry-After`. Requests with a valid key are never throttled this way.",
            "content": {
              "application/problem+json": {
                "examples": {
                  "rate_limited": {
                    "summary": "rate_limited",
                    "value": {
                      "type": "https://partners.cbgc.io/docs/errors.md#rate_limited",
                      "title": "Too many requests",
                      "status": 429,
                      "detail": "Too many requests with an invalid API key from your IP address. Wait for the number of seconds in the Retry-After header.",
                      "code": "rate_limited"
                    }
                  }
                }
              }
            }
          }
        }
      }
    }
  },
  "components": {
    "schemas": {
      "BatchApplicant": {
        "examples": [
          {
            "firstName": "Jordan",
            "lastName": "Kim",
            "email": "jordan.kim@example.com",
            "externalIdentifier": "vol-8841"
          }
        ],
        "required": [
          "firstName",
          "lastName",
          "email"
        ],
        "type": "object",
        "properties": {
          "firstName": {
            "type": "string",
            "description": "Applicant's first name."
          },
          "lastName": {
            "type": "string",
            "description": "Applicant's last name."
          },
          "email": {
            "maxLength": 254,
            "type": "string",
            "description": "Applicant's email address (at most 254 characters, a valid address). The invitation is sent here. Also used to detect duplicates.",
            "format": "email"
          },
          "phone": {
            "type": [
              "null",
              "string"
            ],
            "description": "Optional: the applicant's mobile number, 10-digit US or Canadian in any format. When sent, the invitation is also texted to it."
          },
          "externalIdentifier": {
            "maxLength": 100,
            "type": [
              "null",
              "string"
            ],
            "description": "Your identifier for this applicant's check (max 100 characters). Echoed in the response and webhooks, and used to detect duplicates."
          },
          "jobLocation": {
            "maxLength": 100,
            "type": [
              "null",
              "string"
            ],
            "description": "Where this applicant will work (at most 100 characters), with `stateOfEmployment`. Replaces the batch's `jobLocation` and `stateOfEmployment` for this applicant."
          },
          "stateOfEmployment": {
            "type": [
              "null",
              "string"
            ],
            "description": "The US state this applicant will work in (2-letter code or name), with `jobLocation`."
          }
        },
        "description": "One applicant in a batch order."
      },
      "BatchCompletedWebhook": {
        "examples": [
          {
            "event": "batch.completed",
            "eventId": "0b8f2e6d-4c1a-4f3e-9d7b-5a2c8e1f6d30",
            "timestamp": "2026-10-16T15:12:30Z",
            "batchId": "3f1c0b9e-7a52-4a6e-9d0c-5e7d2b1a8c44",
            "batchReference": "onboarding-2026-10-16",
            "clientId": "cl_5b0e6f2a8c1d4e7b9a3f2d6c",
            "externalClientId": "org-1042",
            "counts": {
              "QUEUED": 0,
              "PENDING": 0,
              "CREATED": 1
            }
          }
        ],
        "type": "object",
        "properties": {
          "event": {
            "enum": [
              "batch.completed"
            ],
            "type": "string",
            "description": "Always `batch.completed`."
          },
          "eventId": {
            "type": "string",
            "description": "Unique id of this event, the same on every retry of its delivery. Use it to drop duplicate deliveries.",
            "format": "uuid"
          },
          "timestamp": {
            "type": "string",
            "description": "When this webhook was generated (UTC).",
            "format": "date-time"
          },
          "batchId": {
            "type": "string",
            "description": "The batch, as returned by `POST /clients/{clientRef}/checks/batch`.",
            "format": "uuid"
          },
          "batchReference": {
            "type": [
              "null",
              "string"
            ],
            "description": "Your `batchReference` for the batch, if you sent one."
          },
          "clientId": {
            "type": "string",
            "description": "The client the batch was ordered for (its `clientId`)."
          },
          "externalClientId": {
            "type": [
              "null",
              "string"
            ],
            "description": "Your own id for the client, if it has one."
          },
          "counts": {
            "type": "object",
            "additionalProperties": {
              "pattern": "^-?(?:0|[1-9]\\d*)$",
              "type": [
                "integer",
                "string"
              ],
              "format": "int32"
            },
            "description": "Number of checks per status at completion, e.g. `{ \"CREATED\": 12 }`."
          }
        },
        "description": "Body of the `batch.completed` webhook: no check of the batch is `QUEUED` or `PENDING` any more."
      },
      "BatchItemResponse": {
        "examples": [
          {
            "index": 1,
            "externalIdentifier": "vol-8842",
            "result": "invalid",
            "errors": {
              "email": [
                "email is not a valid email address."
              ]
            }
          }
        ],
        "type": "object",
        "properties": {
          "index": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "type": [
              "integer",
              "string"
            ],
            "description": "Position of the applicant in the request's `applicants` array, starting at 0.",
            "format": "int32"
          },
          "externalIdentifier": {
            "type": [
              "null",
              "string"
            ],
            "description": "The applicant's `externalIdentifier`, as sent."
          },
          "result": {
            "enum": [
              "queued",
              "duplicate",
              "invalid"
            ],
            "type": "string",
            "description": "`queued`: accepted, will be submitted. `duplicate`: the applicant already has a check in progress or unexpired (`check` is that check) and nothing was ordered. `invalid`: the applicant's data was rejected (see `errors`) and nothing was ordered."
          },
          "check": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "description": "The queued check, or the existing check for a `duplicate`. Absent for `invalid`.",
                "$ref": "#/components/schemas/CheckResponse"
              }
            ],
            "description": "The queued check, or the existing check for a `duplicate`. Absent for `invalid`."
          },
          "errors": {
            "type": [
              "null",
              "object"
            ],
            "additionalProperties": {
              "type": "array",
              "items": {
                "type": "string"
              }
            },
            "description": "For `invalid` items: a map from field name to messages."
          }
        },
        "description": "What happened to one applicant of a batch."
      },
      "BatchOrderRequest": {
        "examples": [
          {
            "level": 2,
            "batchReference": "onboarding-2026-10-16",
            "orderedBy": {
              "email": "dana@gracecommunity.example",
              "firstName": "Dana",
              "lastName": "Lopez"
            },
            "applicants": [
              {
                "firstName": "Jordan",
                "lastName": "Kim",
                "email": "jordan.kim@example.com",
                "externalIdentifier": "vol-8841"
              },
              {
                "firstName": "Sam",
                "lastName": "Ortiz",
                "email": "sam.ortiz@example",
                "externalIdentifier": "vol-8842"
              }
            ]
          }
        ],
        "required": [
          "level",
          "applicants"
        ],
        "type": "object",
        "properties": {
          "level": {
            "maximum": 6,
            "minimum": 1,
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "type": [
              "null",
              "integer",
              "string"
            ],
            "description": "Product level to order for everyone, 1 to 6 (see `GET /products`).",
            "format": "int32"
          },
          "jobLocation": {
            "maxLength": 100,
            "type": [
              "null",
              "string"
            ],
            "description": "Where the applicants will work, e.g. `Blue Springs, MO` (at most 100 characters), for everyone without their own. Some levels require it with `stateOfEmployment`; send both or neither, and only as the church gives it."
          },
          "stateOfEmployment": {
            "type": [
              "null",
              "string"
            ],
            "description": "The US state the applicants will work in (2-letter code or name), with `jobLocation`."
          },
          "orderedBy": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "description": "Optional: who ordered these checks. Written to each order's notes only; never stored and never creates a login.",
                "$ref": "#/components/schemas/OrderedBy"
              }
            ],
            "description": "Optional: who ordered these checks. Written to each order's notes only; never stored and never creates a login."
          },
          "batchReference": {
            "maxLength": 100,
            "type": [
              "null",
              "string"
            ],
            "description": "Your idempotency key for this batch (max 100 characters). Sending the same value again returns the first batch instead of ordering again."
          },
          "applicants": {
            "maxItems": 500,
            "minItems": 1,
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/BatchApplicant"
            },
            "description": "The people to order checks for, 1 to 500."
          }
        },
        "description": "Order checks for up to 500 people in one call, all at the same `level`."
      },
      "BatchResponse": {
        "examples": [
          {
            "batchId": "3f1c0b9e-7a52-4a6e-9d0c-5e7d2b1a8c44",
            "batchReference": "onboarding-2026-10-16",
            "clientId": "cl_5b0e6f2a8c1d4e7b9a3f2d6c",
            "status": "processing",
            "counts": {
              "QUEUED": 1,
              "PENDING": 0,
              "CREATED": 0
            },
            "items": [
              {
                "index": 0,
                "externalIdentifier": "vol-8841",
                "result": "queued",
                "check": {
                  "id": "chk_9c2e4f6a1b3d4e5f8a7b6c5d",
                  "externalIdentifier": "vol-8841",
                  "clientId": "cl_5b0e6f2a8c1d4e7b9a3f2d6c",
                  "status": "QUEUED",
                  "stage": "queued",
                  "level": 2,
                  "expirationDate": null,
                  "completedAt": null,
                  "createdAt": "2026-10-16T15:10:00Z",
                  "updatedAt": "2026-10-16T15:10:00Z"
                }
              },
              {
                "index": 1,
                "externalIdentifier": "vol-8842",
                "result": "invalid",
                "errors": {
                  "email": [
                    "email is not a valid email address."
                  ]
                }
              }
            ],
            "createdAt": "2026-10-16T15:10:00Z"
          }
        ],
        "type": "object",
        "properties": {
          "batchId": {
            "type": "string",
            "description": "Identifier of the batch. Use it in `GET /batches/{batchId}`.",
            "format": "uuid"
          },
          "batchReference": {
            "type": [
              "null",
              "string"
            ],
            "description": "Your `batchReference`, if you sent one."
          },
          "clientId": {
            "type": "string",
            "description": "The client the batch was ordered for (its `clientId`)."
          },
          "status": {
            "enum": [
              "processing",
              "completed"
            ],
            "type": "string",
            "description": "`processing` while any check is `QUEUED` or `PENDING`; `completed` once none is."
          },
          "counts": {
            "type": "object",
            "additionalProperties": {
              "pattern": "^-?(?:0|[1-9]\\d*)$",
              "type": [
                "integer",
                "string"
              ],
              "format": "int32"
            },
            "description": "Number of the batch's checks in each status, e.g. `{ \"QUEUED\": 0, \"PENDING\": 0, \"CREATED\": 12 }`."
          },
          "items": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/BatchItemResponse"
            },
            "description": "One entry per applicant of the request, in request order."
          },
          "createdAt": {
            "type": "string",
            "description": "When the batch was received (UTC).",
            "format": "date-time"
          }
        },
        "description": "A batch order and where its checks stand."
      },
      "CancelCheckResponse": {
        "examples": [
          {
            "id": "chk_9c2e4f6a1b3d4e5f8a7b6c5d",
            "externalIdentifier": "vol-8841",
            "clientId": "cl_5b0e6f2a8c1d4e7b9a3f2d6c",
            "status": "CANCELLED",
            "stage": "closed",
            "level": 2,
            "expirationDate": null,
            "completedAt": "2026-10-16T16:00:00Z",
            "createdAt": "2026-10-16T15:10:00Z",
            "updatedAt": "2026-10-16T16:00:00Z",
            "upstreamCancelled": true
          }
        ],
        "type": "object",
        "properties": {
          "upstreamCancelled": {
            "type": "boolean",
            "description": "Whether the cancellation was confirmed by the screening provider. If false, the check is cancelled here but may still be processed and billed; contact support."
          },
          "id": {
            "type": "string",
            "description": "Identifier of the check (`chk_…`). Can be used as `{checkRef}` in paths."
          },
          "externalIdentifier": {
            "type": [
              "null",
              "string"
            ],
            "description": "Your identifier for this check, as sent when ordering. Can be used as `{checkRef}` in paths."
          },
          "clientId": {
            "type": "string",
            "description": "Client the check belongs to (its `clientId`)."
          },
          "status": {
            "enum": [
              "QUEUED",
              "PENDING",
              "CREATED",
              "CLEAR",
              "REVIEW",
              "ERROR",
              "CANCELLED"
            ],
            "type": "string",
            "description": "`QUEUED`: in a batch, not yet submitted. `PENDING`: accepted, being submitted. `CREATED`: submitted; the applicant has been invited to complete their information. `CLEAR`: completed, no search found records. `REVIEW`: completed and flagged (records found, or the result needs a person to look). `ERROR`: could not be completed. `CANCELLED`: cancelled. `CLEAR`, `REVIEW`, `ERROR` and `CANCELLED` are final."
          },
          "stage": {
            "enum": [
              "queued",
              "submitting",
              "awaiting_applicant",
              "applicant_ready",
              "in_progress",
              "complete",
              "closed"
            ],
            "type": "string",
            "description": "Where the check is, finer than `status`: `queued` (in a batch, not yet sent), `submitting` (being sent), `awaiting_applicant` (invite sent; the applicant hasn't submitted), `applicant_ready` (the applicant submitted, and the church must review and release the order in its portal, e.g. a possible duplicate or a jurisdiction question), `in_progress` (the applicant submitted; searches are running), `complete` (final result ready: `CLEAR` or `REVIEW`), `closed` (will not complete: `ERROR` or `CANCELLED`). `awaiting_applicant`, `applicant_ready` and `in_progress` are all `CREATED`; each move between them sends `check.updated`."
          },
          "level": {
            "maximum": 6,
            "minimum": 1,
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "type": [
              "null",
              "integer",
              "string"
            ],
            "description": "Product level the check was ordered with (1 to 6), or null for a check imported from the church's history.",
            "format": "int32"
          },
          "expirationDate": {
            "type": [
              "null",
              "string"
            ],
            "description": "When a completed check expires (UTC). Until then, re-ordering the same applicant is rejected unless `forceReorder` is set.",
            "format": "date-time"
          },
          "completedAt": {
            "type": [
              "null",
              "string"
            ],
            "description": "When the check reached a final status (UTC).",
            "format": "date-time"
          },
          "reportUrl": {
            "type": [
              "null",
              "string"
            ],
            "description": "Only for `CLEAR` and `REVIEW`: the report in the church's Datasource portal. It needs the church's own sign-in. Null otherwise, or while unknown."
          },
          "charges": {
            "pattern": "^-?(?:0|[1-9]\\d*)(?:\\.\\d+)?$",
            "type": [
              "null",
              "number",
              "string"
            ],
            "description": "Only for a final status: what the screening provider charged for the check, in US dollars. Null while open or unknown.",
            "format": "double"
          },
          "errorCode": {
            "type": [
              "null",
              "string"
            ],
            "description": "Only for `ERROR`: why, as a stable code (`missing_job_location`, `missing_information`, `invalid_applicant_details`, `provider_rejected`, `not_submitted`, `provider_error`, `invitation_expired`; `simulated` in the test environment). Null otherwise."
          },
          "errorMessage": {
            "type": [
              "null",
              "string"
            ],
            "description": "Only for `ERROR`: one sentence for the church's staff saying what went wrong and what to do. Null otherwise."
          },
          "createdAt": {
            "type": "string",
            "description": "When the check was ordered (UTC).",
            "format": "date-time"
          },
          "updatedAt": {
            "type": "string",
            "description": "When the check last changed (UTC).",
            "format": "date-time"
          }
        },
        "description": "A cancelled check."
      },
      "CheckExpiringWebhook": {
        "examples": [
          {
            "event": "check.expiring",
            "eventId": "a4c6e8f0-2b4d-4f6a-8c0e-1a3c5e7f9b2d",
            "timestamp": "2028-09-18T14:00:03Z",
            "id": "chk_9c2e4f6a1b3d4e5f8a7b6c5d",
            "externalIdentifier": "vol-8841",
            "clientId": "cl_5b0e6f2a8c1d4e7b9a3f2d6c",
            "status": "CLEAR",
            "expirationDate": "2028-10-18T18:22:41Z",
            "daysLeft": 30
          }
        ],
        "type": "object",
        "properties": {
          "event": {
            "type": "string",
            "description": "Always `check.expiring`."
          },
          "eventId": {
            "type": "string",
            "description": "Unique id of this event, the same on every retry of its delivery. Use it to drop duplicate deliveries."
          },
          "timestamp": {
            "type": "string",
            "description": "When this webhook was generated (UTC).",
            "format": "date-time"
          },
          "id": {
            "type": "string",
            "description": "The check's `id` (`chk_…`)."
          },
          "externalIdentifier": {
            "type": [
              "null",
              "string"
            ],
            "description": "Your identifier for the check, as sent when ordering."
          },
          "clientId": {
            "type": "string",
            "description": "The client (church) the check belongs to (`cl_…`)."
          },
          "status": {
            "type": "string",
            "description": "`CLEAR` or `REVIEW`."
          },
          "expirationDate": {
            "type": "string",
            "description": "When the check expires (UTC).",
            "format": "date-time"
          },
          "daysLeft": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "type": [
              "integer",
              "string"
            ],
            "description": "Whole days until `expirationDate` (30 or fewer).",
            "format": "int32"
          }
        },
        "description": "Body of the `check.expiring` webhook: a `CLEAR` or `REVIEW` check expires soon. Sent 30 days and 7 days before `expirationDate` (once each)."
      },
      "CheckLookupItem": {
        "examples": [
          {
            "index": 0,
            "check": {
              "id": "chk_9c2e4f6a1b3d4e5f8a7b6c5d",
              "externalIdentifier": null,
              "clientId": "cl_5b0e6f2a8c1d4e7b9a3f2d6c",
              "status": "CLEAR",
              "stage": "complete",
              "level": null,
              "expirationDate": "2028-01-07T18:22:41Z",
              "completedAt": "2026-01-07T18:22:41Z",
              "createdAt": "2026-01-05T15:10:00Z",
              "updatedAt": "2026-01-07T18:22:41Z"
            }
          }
        ],
        "type": "object",
        "properties": {
          "index": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "type": [
              "integer",
              "string"
            ],
            "description": "Position of the email in the request's `emails` array, starting at 0. Emails are never echoed.",
            "format": "int32"
          },
          "check": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "description": "The applicant's most recent check for this client (imported history included), or null if there is none.",
                "$ref": "#/components/schemas/CheckResponse"
              }
            ],
            "description": "The applicant's most recent check for this client (imported history included), or null if there is none."
          }
        },
        "description": "The latest check for one looked-up email."
      },
      "CheckLookupRequest": {
        "examples": [
          {
            "emails": [
              "jordan.kim@example.com",
              "sam.ortiz@example.com"
            ]
          }
        ],
        "required": [
          "emails"
        ],
        "type": "object",
        "properties": {
          "emails": {
            "maxItems": 500,
            "minItems": 1,
            "type": "array",
            "items": {
              "type": "string",
              "format": "email"
            },
            "description": "1 to 500 applicant email addresses. Matching ignores case and surrounding spaces. The response never contains an email."
          }
        },
        "description": "A list of applicant emails to find existing checks for."
      },
      "CheckLookupResponse": {
        "examples": [
          {
            "items": [
              {
                "index": 0,
                "check": {
                  "id": "chk_9c2e4f6a1b3d4e5f8a7b6c5d",
                  "externalIdentifier": null,
                  "clientId": "cl_5b0e6f2a8c1d4e7b9a3f2d6c",
                  "status": "CLEAR",
                  "stage": "complete",
                  "level": null,
                  "expirationDate": "2028-01-07T18:22:41Z",
                  "completedAt": "2026-01-07T18:22:41Z",
                  "createdAt": "2026-01-05T15:10:00Z",
                  "updatedAt": "2026-01-07T18:22:41Z"
                }
              },
              {
                "index": 1,
                "check": null
              }
            ]
          }
        ],
        "type": "object",
        "properties": {
          "items": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/CheckLookupItem"
            },
            "description": "One item per email in the request."
          }
        },
        "description": "The result of a check lookup, one item per requested email, in request order."
      },
      "CheckPage": {
        "examples": [
          {
            "items": [
              {
                "id": "chk_9c2e4f6a1b3d4e5f8a7b6c5d",
                "externalIdentifier": "vol-8841",
                "clientId": "cl_5b0e6f2a8c1d4e7b9a3f2d6c",
                "status": "CLEAR",
                "stage": "complete",
                "level": 2,
                "expirationDate": "2028-10-18T18:22:41Z",
                "completedAt": "2026-10-18T18:22:41Z",
                "createdAt": "2026-10-16T15:10:00Z",
                "updatedAt": "2026-10-18T18:22:41Z"
              }
            ],
            "nextCursor": "NjM4OTYxNjU3NjE0MDAwMDAwOjEwNDI"
          }
        ],
        "type": "object",
        "properties": {
          "items": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/CheckResponse"
            },
            "description": "Checks on this page."
          },
          "nextCursor": {
            "type": [
              "null",
              "string"
            ],
            "description": "Pass as `cursor` to fetch the next page. Null when there are no more results."
          }
        },
        "description": "One page of checks, ordered by `updatedAt` then `id`."
      },
      "CheckResponse": {
        "examples": [
          {
            "id": "chk_9c2e4f6a1b3d4e5f8a7b6c5d",
            "externalIdentifier": "vol-8841",
            "clientId": "cl_5b0e6f2a8c1d4e7b9a3f2d6c",
            "status": "CREATED",
            "stage": "awaiting_applicant",
            "level": 2,
            "expirationDate": null,
            "completedAt": null,
            "createdAt": "2026-10-16T15:10:00Z",
            "updatedAt": "2026-10-16T15:10:01Z"
          }
        ],
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "Identifier of the check (`chk_…`). Can be used as `{checkRef}` in paths."
          },
          "externalIdentifier": {
            "type": [
              "null",
              "string"
            ],
            "description": "Your identifier for this check, as sent when ordering. Can be used as `{checkRef}` in paths."
          },
          "clientId": {
            "type": "string",
            "description": "Client the check belongs to (its `clientId`)."
          },
          "status": {
            "enum": [
              "QUEUED",
              "PENDING",
              "CREATED",
              "CLEAR",
              "REVIEW",
              "ERROR",
              "CANCELLED"
            ],
            "type": "string",
            "description": "`QUEUED`: in a batch, not yet submitted. `PENDING`: accepted, being submitted. `CREATED`: submitted; the applicant has been invited to complete their information. `CLEAR`: completed, no search found records. `REVIEW`: completed and flagged (records found, or the result needs a person to look). `ERROR`: could not be completed. `CANCELLED`: cancelled. `CLEAR`, `REVIEW`, `ERROR` and `CANCELLED` are final."
          },
          "stage": {
            "enum": [
              "queued",
              "submitting",
              "awaiting_applicant",
              "applicant_ready",
              "in_progress",
              "complete",
              "closed"
            ],
            "type": "string",
            "description": "Where the check is, finer than `status`: `queued` (in a batch, not yet sent), `submitting` (being sent), `awaiting_applicant` (invite sent; the applicant hasn't submitted), `applicant_ready` (the applicant submitted, and the church must review and release the order in its portal, e.g. a possible duplicate or a jurisdiction question), `in_progress` (the applicant submitted; searches are running), `complete` (final result ready: `CLEAR` or `REVIEW`), `closed` (will not complete: `ERROR` or `CANCELLED`). `awaiting_applicant`, `applicant_ready` and `in_progress` are all `CREATED`; each move between them sends `check.updated`."
          },
          "level": {
            "maximum": 6,
            "minimum": 1,
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "type": [
              "null",
              "integer",
              "string"
            ],
            "description": "Product level the check was ordered with (1 to 6), or null for a check imported from the church's history.",
            "format": "int32"
          },
          "expirationDate": {
            "type": [
              "null",
              "string"
            ],
            "description": "When a completed check expires (UTC). Until then, re-ordering the same applicant is rejected unless `forceReorder` is set.",
            "format": "date-time"
          },
          "completedAt": {
            "type": [
              "null",
              "string"
            ],
            "description": "When the check reached a final status (UTC).",
            "format": "date-time"
          },
          "reportUrl": {
            "type": [
              "null",
              "string"
            ],
            "description": "Only for `CLEAR` and `REVIEW`: the report in the church's Datasource portal. It needs the church's own sign-in. Null otherwise, or while unknown."
          },
          "charges": {
            "pattern": "^-?(?:0|[1-9]\\d*)(?:\\.\\d+)?$",
            "type": [
              "null",
              "number",
              "string"
            ],
            "description": "Only for a final status: what the screening provider charged for the check, in US dollars. Null while open or unknown.",
            "format": "double"
          },
          "errorCode": {
            "type": [
              "null",
              "string"
            ],
            "description": "Only for `ERROR`: why, as a stable code (`missing_job_location`, `missing_information`, `invalid_applicant_details`, `provider_rejected`, `not_submitted`, `provider_error`, `invitation_expired`; `simulated` in the test environment). Null otherwise."
          },
          "errorMessage": {
            "type": [
              "null",
              "string"
            ],
            "description": "Only for `ERROR`: one sentence for the church's staff saying what went wrong and what to do. Null otherwise."
          },
          "createdAt": {
            "type": "string",
            "description": "When the check was ordered (UTC).",
            "format": "date-time"
          },
          "updatedAt": {
            "type": "string",
            "description": "When the check last changed (UTC).",
            "format": "date-time"
          }
        },
        "description": "The queued check, or the existing check for a `duplicate`. Absent for `invalid`."
      },
      "ClientConnectedWebhook": {
        "examples": [
          {
            "event": "client.connected",
            "eventId": "6c3d9a1e-8b2f-4e5d-a7c0-1f9e3b5d7a24",
            "timestamp": "2026-10-16T15:16:40Z",
            "sessionId": "8d2f6c14-3b7a-4e0e-9c55-1a9b7d3e2f60",
            "externalClientId": "org-1042",
            "clientId": "cl_5b0e6f2a8c1d4e7b9a3f2d6c",
            "status": "Active"
          }
        ],
        "type": "object",
        "properties": {
          "event": {
            "enum": [
              "client.connected"
            ],
            "type": "string",
            "description": "Always `client.connected`."
          },
          "eventId": {
            "type": "string",
            "description": "Unique id of this event, the same on every retry of its delivery. Use it to drop duplicate deliveries.",
            "format": "uuid"
          },
          "timestamp": {
            "type": "string",
            "description": "When this webhook was generated (UTC).",
            "format": "date-time"
          },
          "sessionId": {
            "type": "string",
            "description": "The connect session that created the client.",
            "format": "uuid"
          },
          "externalClientId": {
            "type": "string",
            "description": "The `externalClientId` you sent when creating the session."
          },
          "clientId": {
            "type": "string",
            "description": "The new client's `clientId`."
          },
          "status": {
            "enum": [
              "Active"
            ],
            "type": "string",
            "description": "The client's status. Always `Active` for a newly connected church."
          }
        },
        "description": "Body of the `client.connected` webhook: a church finished the connect page and its client now exists."
      },
      "ClientMigratedWebhook": {
        "examples": [
          {
            "event": "client.migrated",
            "eventId": "a4e2c8f0-3d6b-4a1e-b9c5-7d0f2e4a6c81",
            "timestamp": "2026-10-20T14:02:11Z",
            "externalClientId": "org-1042",
            "clientId": "cl_5b0e6f2a8c1d4e7b9a3f2d6c",
            "importedChecks": 214
          }
        ],
        "type": "object",
        "properties": {
          "event": {
            "enum": [
              "client.migrated"
            ],
            "type": "string",
            "description": "Always `client.migrated`."
          },
          "eventId": {
            "type": "string",
            "description": "Unique id of this event, the same on every retry of its delivery. Use it to drop duplicate deliveries.",
            "format": "uuid"
          },
          "timestamp": {
            "type": "string",
            "description": "When this webhook was generated (UTC).",
            "format": "date-time"
          },
          "externalClientId": {
            "type": "string",
            "description": "The `externalClientId` you sent when creating the connect session."
          },
          "clientId": {
            "type": "string",
            "description": "The moved client's `clientId`."
          },
          "importedChecks": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "type": [
              "integer",
              "string"
            ],
            "description": "How many past checks were imported for the client.",
            "format": "int32"
          }
        },
        "description": "Body of the `client.migrated` webhook: an existing Datasource church was moved under your account and its past checks were imported."
      },
      "ClientResponse": {
        "examples": [
          {
            "clientId": "cl_5b0e6f2a8c1d4e7b9a3f2d6c",
            "externalClientId": "org-1042",
            "name": "Grace Community Church",
            "status": "Active",
            "createdAt": "2026-10-16T15:04:05Z",
            "portalUrl": "https://portal.example.com"
          }
        ],
        "type": "object",
        "properties": {
          "clientId": {
            "type": "string",
            "description": "Identifier of the client (`cl_…`). Can be used as `{clientRef}` in the path of every client and check operation."
          },
          "externalClientId": {
            "type": [
              "null",
              "string"
            ],
            "description": "Your own identifier for the client, if you sent one. Can be used as `{clientRef}` in paths."
          },
          "name": {
            "type": [
              "null",
              "string"
            ],
            "description": "The client's display name, if known."
          },
          "status": {
            "type": "string",
            "description": "Client status, e.g. `Active`."
          },
          "createdAt": {
            "type": "string",
            "description": "When the client was created (UTC).",
            "format": "date-time"
          },
          "portalUrl": {
            "type": [
              "null",
              "string"
            ],
            "description": "Where the client's staff sign in to the portal (open it in a new tab; they use their own login). Null if no portal address is configured.",
            "format": "uri"
          }
        },
        "description": "One of your customers, provisioned as a client under your account."
      },
      "ClientSettingsRequest": {
        "type": "object",
        "properties": {
          "checkValidityYears": {
            "maximum": 5,
            "minimum": 1,
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "type": [
              "null",
              "integer",
              "string"
            ],
            "description": "How many years this client's `CLEAR` or `REVIEW` checks stay valid: their `expirationDate` is completion plus this. A whole number from 1 to 5, or null (or omitted) to use your account's default (`checkValidityYears` in `PUT /settings`). Applies to checks completed after the change.",
            "format": "int32"
          }
        },
        "description": "One client's settings."
      },
      "ClientSettingsResponse": {
        "type": "object",
        "properties": {
          "clientId": {
            "type": "string",
            "description": "The client's `id` (`cl_…`)."
          },
          "checkValidityYears": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "type": [
              "null",
              "integer",
              "string"
            ],
            "description": "The client's own validity in years, or null if it uses your account's default.",
            "format": "int32"
          },
          "effectiveCheckValidityYears": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "type": [
              "integer",
              "string"
            ],
            "description": "The validity in years that applies to this client: its own, else your account's default.",
            "format": "int32"
          }
        },
        "description": "One client's settings, and the values that apply to it."
      },
      "ConnectPrefill": {
        "examples": [
          {
            "contactName": "Dana Lopez",
            "contactEmail": "dana@gracecommunity.example",
            "contactPhone": "(303) 555-0142"
          }
        ],
        "type": "object",
        "properties": {
          "contactName": {
            "maxLength": 200,
            "type": [
              "null",
              "string"
            ],
            "description": "Primary contact's full name."
          },
          "contactEmail": {
            "maxLength": 200,
            "type": [
              "null",
              "string"
            ],
            "description": "Primary contact's email address. It becomes their sign-in.",
            "format": "email"
          },
          "contactPhone": {
            "maxLength": 200,
            "type": [
              "null",
              "string"
            ],
            "description": "Primary contact's phone number."
          }
        },
        "description": "Optional details to pre-fill on the contact step."
      },
      "ConnectSessionRequest": {
        "examples": [
          {
            "externalClientId": "org-1042",
            "churchName": "Grace Community Church",
            "returnUrl": "https://app.example.com/cbc/connect-done",
            "prefill": {
              "contactName": "Dana Lopez",
              "contactEmail": "dana@gracecommunity.example",
              "contactPhone": "(303) 555-0142"
            }
          }
        ],
        "required": [
          "externalClientId",
          "churchName",
          "returnUrl"
        ],
        "type": "object",
        "properties": {
          "externalClientId": {
            "maxLength": 100,
            "type": "string",
            "description": "Your own id for the church (max 100 characters). It becomes the client's `externalClientId`."
          },
          "churchName": {
            "maxLength": 200,
            "type": "string",
            "description": "The church's name (max 200 characters), shown read-only on the page."
          },
          "prefill": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "description": "Optional details to pre-fill on the contact step.",
                "$ref": "#/components/schemas/ConnectPrefill"
              }
            ],
            "description": "Optional details to pre-fill on the contact step."
          },
          "returnUrl": {
            "type": "string",
            "description": "Where to send the browser when the church is done. Must exactly match one of your registered `connectReturnUrls` (see `PUT /settings`). We add `session_id` and `state`.",
            "format": "uri"
          }
        },
        "description": "Starts a connect session for one church. The browser is then sent to the returned `connectUrl`."
      },
      "ConnectSessionResponse": {
        "examples": [
          {
            "sessionId": "8d2f6c14-3b7a-4e0e-9c55-1a9b7d3e2f60",
            "connectUrl": "https://dev.partners.cbgc.io/connect?t=Zk3qL0b2m9Xr7Tn4Vw1Yc6Hs8Pd5Ja2Ue0Gi3Ko7Mf9",
            "expiresAt": "2026-10-16T15:19:05Z"
          }
        ],
        "type": "object",
        "properties": {
          "sessionId": {
            "type": "string",
            "description": "Identifier of the session. Use it in `GET /connect/sessions/{sessionId}` and to match the redirect back to you.",
            "format": "uuid"
          },
          "connectUrl": {
            "type": "string",
            "description": "Send the church's browser here (a normal top-level navigation, no iframe). Single use: it works once, within 15 minutes.",
            "format": "uri"
          },
          "expiresAt": {
            "type": "string",
            "description": "When the link stops working if it hasn't been opened (UTC).",
            "format": "date-time"
          }
        },
        "description": "A newly created connect session."
      },
      "ConnectSessionStatusResponse": {
        "examples": [
          {
            "sessionId": "8d2f6c14-3b7a-4e0e-9c55-1a9b7d3e2f60",
            "externalClientId": "org-1042",
            "state": "completed",
            "clientId": "cl_5b0e6f2a8c1d4e7b9a3f2d6c"
          }
        ],
        "type": "object",
        "properties": {
          "sessionId": {
            "type": "string",
            "description": "Identifier of the session.",
            "format": "uuid"
          },
          "externalClientId": {
            "type": "string",
            "description": "The `externalClientId` you sent when creating the session."
          },
          "state": {
            "enum": [
              "created",
              "opened",
              "completed",
              "cancelled",
              "expired",
              "migration_requested"
            ],
            "type": "string",
            "description": "`created`: waiting for the church to open the link. `opened`: the church is filling in the page. `completed`: the client exists (see `clientId`). `cancelled`: the church cancelled. `expired`: the link or session ran out, or it was locked after too many failed attempts. `migration_requested`: the church said it is already a Datasource customer; we are verifying it by hand."
          },
          "clientId": {
            "type": [
              "null",
              "string"
            ],
            "description": "The new client's `clientId`. Null until the session is `completed`."
          }
        },
        "description": "Where a connect session stands. Use it (or the `client.connected` webhook) to confirm a church is connected."
      },
      "CreateClientRequest": {
        "examples": [
          {
            "externalClientId": "org-1042",
            "legalCompanyName": "Grace Community Church",
            "dbaCompanyName": "Grace Community",
            "primaryContactFirstName": "Dana",
            "primaryContactLastName": "Lopez",
            "primaryContactEmail": "dana@gracecommunity.example",
            "primaryContactPhone": "(303) 555-0142",
            "streetLine1": "1200 Larimer St",
            "streetLine2": "Suite 300",
            "city": "Denver",
            "state": "CO",
            "postalCode": "80202",
            "country": "US"
          }
        ],
        "required": [
          "legalCompanyName",
          "primaryContactFirstName",
          "primaryContactLastName",
          "primaryContactEmail",
          "primaryContactPhone",
          "streetLine1",
          "city",
          "state",
          "postalCode"
        ],
        "type": "object",
        "properties": {
          "externalClientId": {
            "maxLength": 100,
            "type": [
              "null",
              "string"
            ],
            "description": "Your own identifier for this customer (max 100 characters, unique among your clients). Lets you address the client by your id instead of ours: use it wherever `{clientRef}` appears."
          },
          "legalCompanyName": {
            "type": "string",
            "description": "Legal registered entity name of the company or organization."
          },
          "dbaCompanyName": {
            "type": [
              "null",
              "string"
            ],
            "description": "Optional 'Doing Business As' (DBA) or trade name."
          },
          "primaryContactFirstName": {
            "type": "string",
            "description": "First name of the primary administrator contact."
          },
          "primaryContactLastName": {
            "type": "string",
            "description": "Last name of the primary administrator contact."
          },
          "primaryContactEmail": {
            "type": "string",
            "description": "Email address of the primary contact. Becomes the username of their portal login.",
            "format": "email"
          },
          "primaryContactPhone": {
            "type": [
              "null",
              "string"
            ],
            "description": "Phone number of the primary contact."
          },
          "companyPhone": {
            "type": [
              "null",
              "string"
            ],
            "description": "Optional main company phone number."
          },
          "streetLine1": {
            "type": "string",
            "description": "Physical street address (line 1)."
          },
          "streetLine2": {
            "type": [
              "null",
              "string"
            ],
            "description": "Optional physical street address (line 2, suite/unit)."
          },
          "city": {
            "type": "string",
            "description": "Physical city."
          },
          "state": {
            "type": "string",
            "description": "State or province: 2-letter code (e.g. `CO`) or full name."
          },
          "postalCode": {
            "type": "string",
            "description": "Physical postal / ZIP code."
          },
          "country": {
            "type": "string",
            "description": "Country code (US or CA). Defaults to `US`."
          },
          "billingStreetLine1": {
            "type": [
              "null",
              "string"
            ],
            "description": "Optional separate billing street address (line 1). If omitted, physical address is used."
          },
          "billingStreetLine2": {
            "type": [
              "null",
              "string"
            ],
            "description": "Optional separate billing street address (line 2)."
          },
          "billingCity": {
            "type": [
              "null",
              "string"
            ],
            "description": "Optional separate billing city."
          },
          "billingState": {
            "type": [
              "null",
              "string"
            ],
            "description": "Optional separate billing state."
          },
          "billingPostalCode": {
            "type": [
              "null",
              "string"
            ],
            "description": "Optional separate billing postal / ZIP code."
          },
          "billingCountry": {
            "type": [
              "null",
              "string"
            ],
            "description": "Optional separate billing country code (defaults to 'US')."
          }
        },
        "description": "Details of the customer to provision as a client under your account. A portal login is created for the primary contact."
      },
      "CreateClientResponse": {
        "examples": [
          {
            "clientId": "cl_5b0e6f2a8c1d4e7b9a3f2d6c",
            "externalClientId": "org-1042",
            "name": "Grace Community Church",
            "status": "ACTIVE",
            "createdAt": "2026-10-16T15:04:05Z",
            "portalUrl": "https://portal.example.com",
            "primaryUsername": "dana@gracecommunity.example",
            "welcomeEmailSent": true
          }
        ],
        "type": "object",
        "properties": {
          "primaryUsername": {
            "type": [
              "null",
              "string"
            ],
            "description": "Username of the portal login created for the primary contact: their email address, or a derived name such as `dana-3f9a1c` if that email is already a login. Null if the login couldn't be created (contact support)."
          },
          "welcomeEmailSent": {
            "type": "boolean",
            "description": "Whether the welcome email with the sign-in details was sent to the primary contact. If false, contact support to re-send it."
          },
          "clientId": {
            "type": "string",
            "description": "Identifier of the client (`cl_…`). Can be used as `{clientRef}` in the path of every client and check operation."
          },
          "externalClientId": {
            "type": [
              "null",
              "string"
            ],
            "description": "Your own identifier for the client, if you sent one. Can be used as `{clientRef}` in paths."
          },
          "name": {
            "type": [
              "null",
              "string"
            ],
            "description": "The client's display name, if known."
          },
          "status": {
            "type": "string",
            "description": "Client status, e.g. `Active`."
          },
          "createdAt": {
            "type": "string",
            "description": "When the client was created (UTC).",
            "format": "date-time"
          },
          "portalUrl": {
            "type": [
              "null",
              "string"
            ],
            "description": "Where the client's staff sign in to the portal (open it in a new tab; they use their own login). Null if no portal address is configured.",
            "format": "uri"
          }
        },
        "description": "A newly created client, with the username of the portal login created for its primary contact. The contact receives their sign-in details (with a temporary password) by email from us; no password is ever returned by the API."
      },
      "CreateOrderRequest": {
        "examples": [
          {
            "externalIdentifier": "vol-8841",
            "firstName": "Jordan",
            "lastName": "Kim",
            "applicantEmail": "jordan.kim@example.com",
            "applicantPhone": "801-555-1234",
            "level": 2,
            "jobLocation": "Blue Springs, MO",
            "stateOfEmployment": "MO",
            "orderedBy": {
              "email": "dana@gracecommunity.example",
              "firstName": "Dana",
              "lastName": "Lopez"
            },
            "forceReorder": false
          }
        ],
        "required": [
          "applicantEmail",
          "firstName",
          "lastName",
          "level"
        ],
        "type": "object",
        "properties": {
          "externalIdentifier": {
            "maxLength": 100,
            "type": [
              "null",
              "string"
            ],
            "description": "Your unique identifier for this check (max 100 characters). Strongly recommended: retries with the same value never create a second check, and it is echoed in responses and webhooks."
          },
          "applicantEmail": {
            "type": "string",
            "description": "Applicant's email address. The invitation is sent here. Also used to detect duplicate orders for the same applicant.",
            "format": "email"
          },
          "applicantPhone": {
            "type": [
              "null",
              "string"
            ],
            "description": "Optional: the applicant's mobile number, 10-digit US or Canadian in any format (e.g. `801-555-1234`). When sent, the invitation is also texted to it; the email is still sent. Never stored by us."
          },
          "firstName": {
            "type": "string",
            "description": "Applicant's first name."
          },
          "lastName": {
            "type": "string",
            "description": "Applicant's last name."
          },
          "level": {
            "maximum": 6,
            "minimum": 1,
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "type": [
              "null",
              "integer",
              "string"
            ],
            "description": "Product level to order, 1 to 6 (see `GET /products`).",
            "format": "int32"
          },
          "jobLocation": {
            "maxLength": 100,
            "type": [
              "null",
              "string"
            ],
            "description": "Where the applicant will work, e.g. `Blue Springs, MO` (at most 100 characters). Some levels require it with `stateOfEmployment`; send both or neither. Send it only as the church gives it."
          },
          "stateOfEmployment": {
            "type": [
              "null",
              "string"
            ],
            "description": "The US state the applicant will work in (the employment jurisdiction), as a 2-letter code such as `MO` or a state name. Send with `jobLocation`."
          },
          "orderedBy": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "description": "Optional: who ordered this check. It is written to the order's notes so the client's staff can see who asked. Never stored by us and never creates a login.",
                "$ref": "#/components/schemas/OrderedBy"
              }
            ],
            "description": "Optional: who ordered this check. It is written to the order's notes so the client's staff can see who asked. Never stored by us and never creates a login."
          },
          "forceReorder": {
            "type": "boolean",
            "description": "Order even if the applicant already has an in-progress or unexpired check. Defaults to false. A forced re-order is billed as a new check."
          }
        },
        "description": "A background check to order for an applicant. The applicant is emailed an invitation to provide their details and consent."
      },
      "HttpValidationProblemDetails": {
        "examples": [
          {
            "type": "https://partners.cbgc.io/docs/errors.md#validation_failed",
            "title": "One or more validation errors occurred.",
            "status": 400,
            "errors": {
              "level": [
                "level is required."
              ]
            },
            "code": "validation_failed"
          }
        ],
        "type": "object",
        "properties": {
          "type": {
            "type": [
              "null",
              "string"
            ]
          },
          "title": {
            "type": [
              "null",
              "string"
            ]
          },
          "status": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "type": [
              "null",
              "integer",
              "string"
            ],
            "format": "int32"
          },
          "detail": {
            "type": [
              "null",
              "string"
            ]
          },
          "instance": {
            "type": [
              "null",
              "string"
            ]
          },
          "errors": {
            "type": "object",
            "additionalProperties": {
              "type": "array",
              "items": {
                "type": "string"
              }
            }
          }
        }
      },
      "OrderConflictResponse": {
        "examples": [
          {
            "code": "duplicate_check",
            "message": "A background check is currently in progress with status 'CREATED'.",
            "currentStatus": "CREATED",
            "instructions": "To force a re-order, submit the request with 'forceReorder': true.",
            "order": {
              "id": "chk_9c2e4f6a1b3d4e5f8a7b6c5d",
              "externalIdentifier": "vol-8841",
              "clientId": "cl_5b0e6f2a8c1d4e7b9a3f2d6c",
              "status": "CREATED",
              "stage": "awaiting_applicant",
              "level": 2,
              "expirationDate": null,
              "completedAt": null,
              "createdAt": "2026-10-16T15:10:00Z",
              "updatedAt": "2026-10-16T15:10:01Z"
            }
          }
        ],
        "type": "object",
        "properties": {
          "code": {
            "enum": [
              "duplicate_check"
            ],
            "type": "string",
            "description": "Always `duplicate_check`: the stable error code (see the error catalog)."
          },
          "message": {
            "type": "string",
            "description": "Why the order was rejected."
          },
          "currentStatus": {
            "enum": [
              "QUEUED",
              "PENDING",
              "CREATED",
              "CLEAR",
              "REVIEW",
              "ERROR",
              "CANCELLED"
            ],
            "type": "string",
            "description": "Status of the existing check."
          },
          "instructions": {
            "type": "string",
            "description": "How to proceed if you do want a new check."
          },
          "order": {
            "description": "The existing check.",
            "$ref": "#/components/schemas/CheckResponse"
          }
        },
        "description": "Returned when the applicant already has an in-progress or unexpired check. No new check was ordered and nothing was billed."
      },
      "OrderedBy": {
        "examples": [
          {
            "email": "dana@gracecommunity.example",
            "firstName": "Dana",
            "lastName": "Lopez"
          }
        ],
        "type": "object",
        "properties": {
          "email": {
            "maxLength": 200,
            "type": [
              "null",
              "string"
            ],
            "description": "Email address of the person who ordered the check (optional, at most 200 characters).",
            "format": "email"
          },
          "firstName": {
            "maxLength": 200,
            "type": [
              "null",
              "string"
            ],
            "description": "First name of the person who ordered the check (optional, at most 200 characters)."
          },
          "lastName": {
            "maxLength": 200,
            "type": [
              "null",
              "string"
            ],
            "description": "Last name of the person who ordered the check (optional, at most 200 characters)."
          }
        },
        "description": "Optional: who ordered these checks. Written to each order's notes only; never stored and never creates a login."
      },
      "PartnerWebhookSecretResponse": {
        "examples": [
          {
            "webhookSecret": "whsec_YOUR_SECRET"
          }
        ],
        "type": "object",
        "properties": {
          "webhookSecret": {
            "type": "string",
            "description": "The webhook secret (`whsec_...`). Shown only once; store it now."
          }
        },
        "description": "A newly created webhook signing secret. Returned only once."
      },
      "PingWebhook": {
        "examples": [
          {
            "event": "ping",
            "eventId": "f1d3b5a7-9c2e-4d6f-8a0b-3c5e7f9a1b2d",
            "timestamp": "2026-10-16T15:00:00Z"
          }
        ],
        "type": "object",
        "properties": {
          "event": {
            "enum": [
              "ping"
            ],
            "type": "string",
            "description": "Always `ping`."
          },
          "eventId": {
            "type": "string",
            "description": "Unique id of this event, the same on every retry of its delivery. Use it to drop duplicate deliveries.",
            "format": "uuid"
          },
          "timestamp": {
            "type": "string",
            "description": "When this webhook was generated (UTC).",
            "format": "date-time"
          }
        },
        "description": "Body of the `ping` webhook, sent by `POST /settings/webhook-test` so you can verify your endpoint and signature check."
      },
      "ProblemDetails": {
        "examples": [
          {
            "type": "https://partners.cbgc.io/docs/errors.md#client_not_found",
            "title": "Client not found",
            "status": 404,
            "detail": "No client 'org-1042' exists under your account.",
            "code": "client_not_found"
          }
        ],
        "type": "object",
        "properties": {
          "type": {
            "type": [
              "null",
              "string"
            ]
          },
          "title": {
            "type": [
              "null",
              "string"
            ]
          },
          "status": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "type": [
              "null",
              "integer",
              "string"
            ],
            "format": "int32"
          },
          "detail": {
            "type": [
              "null",
              "string"
            ]
          },
          "instance": {
            "type": [
              "null",
              "string"
            ]
          }
        }
      },
      "ProductResponse": {
        "examples": [
          {
            "level": 2,
            "name": "Standard",
            "description": "National criminal database, county search, SSN trace.",
            "price": 34.95,
            "feesNote": "Plus pass-through fees."
          }
        ],
        "type": "object",
        "properties": {
          "level": {
            "maximum": 6,
            "minimum": 1,
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "type": [
              "integer",
              "string"
            ],
            "description": "The level, 1 to 6. Send it as `level` when ordering a check.",
            "format": "int32"
          },
          "name": {
            "type": "string",
            "description": "Product name, as Datasource names the package (for example \"Employee - Enhanced Nationwide & US Sex Offender\"). The price is in `price`, not in the name."
          },
          "description": {
            "type": "string",
            "description": "What the level covers."
          },
          "price": {
            "pattern": "^-?(?:0|[1-9]\\d*)(?:\\.\\d+)?$",
            "type": [
              "null",
              "number",
              "string"
            ],
            "description": "Base price in USD, or null if no price is available for it.",
            "format": "double"
          },
          "feesNote": {
            "enum": [
              "Plus pass-through fees."
            ],
            "type": "string",
            "description": "Always \"Plus pass-through fees.\": third-party fees are added to the base price."
          }
        },
        "description": "A product level you can order: send `level` when ordering a check."
      },
      "RemindCheckResponse": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "The check's `id` (`chk_…`)."
          },
          "remindedAt": {
            "type": "string",
            "description": "When the invitation was re-sent (UTC). Another reminder is allowed an hour later.",
            "format": "date-time"
          }
        },
        "description": "The applicant's invitation was sent again."
      },
      "ReplayWebhookResponse": {
        "examples": [
          {
            "message": "Webhook replay request accepted",
            "id": "chk_9c2e4f6a1b3d4e5f8a7b6c5d",
            "status": "QUEUED"
          }
        ],
        "type": "object",
        "properties": {
          "message": {
            "type": "string",
            "description": "Human-readable result."
          },
          "id": {
            "type": "string",
            "description": "The check's `id`."
          },
          "status": {
            "enum": [
              "QUEUED"
            ],
            "type": "string",
            "description": "Always `QUEUED`: delivery happens in the background, with retries."
          }
        },
        "description": "Acknowledgement that a webhook re-delivery was queued."
      },
      "SettingsRequest": {
        "examples": [
          {
            "webhookUrl": "https://app.example.com/cbc/webhooks",
            "connectReturnUrls": [
              "https://app.example.com/cbc/connect-done"
            ]
          }
        ],
        "type": "object",
        "properties": {
          "webhookUrl": {
            "type": [
              "null",
              "string"
            ],
            "description": "https:// URL that receives webhooks. Its host must resolve only to public addresses. Empty or omitted turns webhooks off.",
            "format": "uri"
          },
          "connectReturnUrls": {
            "maxItems": 10,
            "type": [
              "null",
              "array"
            ],
            "items": {
              "type": "string",
              "format": "uri"
            },
            "description": "Up to 10 https:// URLs the connect page may send a browser back to. A `returnUrl` must match one exactly."
          },
          "checkValidityYears": {
            "maximum": 5,
            "minimum": 1,
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "type": [
              "null",
              "integer",
              "string"
            ],
            "description": "Your account's default for how many years a `CLEAR` or `REVIEW` check stays valid: its `expirationDate` is its completion plus this. A whole number from 1 to 5. A client's own setting (`PUT /clients/{clientRef}/settings`) overrides it. Omitted keeps the current value (default 2). Applies to checks completed after the change.",
            "format": "int32"
          }
        },
        "description": "Where we send webhooks and which return URLs the connect page may send a browser back to."
      },
      "SettingsResponse": {
        "examples": [
          {
            "webhookUrl": "https://app.example.com/cbc/webhooks",
            "connectReturnUrls": [
              "https://app.example.com/cbc/connect-done"
            ]
          }
        ],
        "type": "object",
        "properties": {
          "webhookUrl": {
            "type": [
              "null",
              "string"
            ],
            "description": "The URL webhooks are sent to, or null if none is registered.",
            "format": "uri"
          },
          "connectReturnUrls": {
            "type": "array",
            "items": {
              "type": "string",
              "format": "uri"
            },
            "description": "The return URLs registered for the connect page."
          },
          "checkValidityYears": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "type": [
              "integer",
              "string"
            ],
            "description": "Your account's default for how many years a `CLEAR` or `REVIEW` check stays valid (1 to 5), for clients without their own setting.",
            "format": "int32"
          }
        },
        "description": "Your registered webhook URL, connect return URLs and how long completed checks stay valid."
      },
      "UpdateCheckRequest": {
        "examples": [
          {
            "externalIdentifier": "vol-8841"
          }
        ],
        "required": [
          "externalIdentifier"
        ],
        "type": "object",
        "properties": {
          "externalIdentifier": {
            "maxLength": 100,
            "type": [
              "null",
              "string"
            ],
            "description": "Your identifier for the check (max 100 characters). Allowed only while the check has none, and it must be unused by your other checks."
          }
        },
        "description": "Sets your own identifier on a check that doesn't have one yet."
      },
      "WebhookDeliveryResponse": {
        "type": "object",
        "properties": {
          "event": {
            "type": "string",
            "description": "The event, e.g. `check.updated`."
          },
          "eventId": {
            "type": [
              "null",
              "string"
            ],
            "description": "The event's `eventId`, the same on every attempt."
          },
          "checkId": {
            "type": [
              "null",
              "string"
            ],
            "description": "The check's `id` for check events; null for others."
          },
          "attempt": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "type": [
              "integer",
              "string"
            ],
            "description": "1 for the first attempt of this event, then 2, 3... (up to 6: the first try and 5 retries).",
            "format": "int32"
          },
          "attemptedAt": {
            "type": "string",
            "description": "When the attempt was made (UTC).",
            "format": "date-time"
          },
          "statusCode": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "type": [
              "null",
              "integer",
              "string"
            ],
            "description": "Your endpoint's HTTP status, or null if it didn't answer (timeout, connection refused).",
            "format": "int32"
          },
          "succeeded": {
            "type": "boolean",
            "description": "Whether your endpoint answered 2xx."
          },
          "error": {
            "type": [
              "null",
              "string"
            ],
            "description": "For a failed attempt, a short reason (e.g. `HTTP 500`, `timeout`). Your response body is never stored."
          },
          "durationMs": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "type": [
              "integer",
              "string"
            ],
            "description": "How long the attempt took, in milliseconds.",
            "format": "int32"
          }
        },
        "description": "One attempt to deliver a webhook to your endpoint."
      },
      "WebhookPayload": {
        "examples": [
          {
            "event": "check.updated",
            "eventId": "e7a1c3b5-2d4f-4a6b-8c9d-0e1f2a3b4c5d",
            "id": "chk_9c2e4f6a1b3d4e5f8a7b6c5d",
            "externalIdentifier": "vol-8841",
            "clientId": "cl_5b0e6f2a8c1d4e7b9a3f2d6c",
            "status": "CLEAR",
            "stage": "complete",
            "level": 2,
            "expirationDate": "2028-10-18T18:22:41Z",
            "completedAt": "2026-10-18T18:22:41Z",
            "timestamp": "2026-10-18T18:22:42Z"
          }
        ],
        "type": "object",
        "properties": {
          "event": {
            "enum": [
              "check.updated"
            ],
            "type": "string",
            "description": "Event type. Currently always `check.updated`."
          },
          "eventId": {
            "type": "string",
            "description": "Unique id of this event, the same on every retry of its delivery. Use it to drop duplicate deliveries.",
            "format": "uuid"
          },
          "id": {
            "type": "string",
            "description": "Identifier of the check (`chk_…`, same as `id` in API responses)."
          },
          "externalIdentifier": {
            "type": [
              "null",
              "string"
            ],
            "description": "Your identifier for the check, as sent when ordering. The easiest way to match the webhook to your records."
          },
          "clientId": {
            "type": "string",
            "description": "Client the check belongs to (its `clientId`)."
          },
          "status": {
            "enum": [
              "QUEUED",
              "PENDING",
              "CREATED",
              "CLEAR",
              "REVIEW",
              "ERROR",
              "CANCELLED"
            ],
            "type": "string",
            "description": "`QUEUED`: in a batch, not yet submitted. `PENDING`: accepted, being submitted. `CREATED`: submitted; the applicant has been invited to complete their information. `CLEAR`: completed, no search found records. `REVIEW`: completed and flagged (records found, or the result needs a person to look). `ERROR`: could not be completed. `CANCELLED`: cancelled. `CLEAR`, `REVIEW`, `ERROR` and `CANCELLED` are final."
          },
          "stage": {
            "enum": [
              "queued",
              "submitting",
              "awaiting_applicant",
              "applicant_ready",
              "in_progress",
              "complete",
              "closed"
            ],
            "type": "string",
            "description": "Where the check is, finer than `status`: `queued` (in a batch, not yet sent), `submitting` (being sent), `awaiting_applicant` (invite sent; the applicant hasn't submitted), `applicant_ready` (the applicant submitted, and the church must review and release the order in its portal, e.g. a possible duplicate or a jurisdiction question), `in_progress` (the applicant submitted; searches are running), `complete` (final result ready: `CLEAR` or `REVIEW`), `closed` (will not complete: `ERROR` or `CANCELLED`). `awaiting_applicant`, `applicant_ready` and `in_progress` are all `CREATED`; each move between them sends `check.updated`."
          },
          "level": {
            "maximum": 6,
            "minimum": 1,
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "type": [
              "null",
              "integer",
              "string"
            ],
            "description": "Product level the check was ordered with (1 to 6), or null for a check imported from the church's history.",
            "format": "int32"
          },
          "expirationDate": {
            "type": [
              "null",
              "string"
            ],
            "description": "When a completed check expires (UTC).",
            "format": "date-time"
          },
          "completedAt": {
            "type": [
              "null",
              "string"
            ],
            "description": "When the check reached a final status (UTC).",
            "format": "date-time"
          },
          "reportUrl": {
            "type": [
              "null",
              "string"
            ],
            "description": "Only for `CLEAR` and `REVIEW`: the report in the church's Datasource portal (needs the church's sign-in). Null otherwise."
          },
          "charges": {
            "pattern": "^-?(?:0|[1-9]\\d*)(?:\\.\\d+)?$",
            "type": [
              "null",
              "number",
              "string"
            ],
            "description": "Only for a final status: what the screening provider charged, in US dollars. Null otherwise.",
            "format": "double"
          },
          "errorCode": {
            "type": [
              "null",
              "string"
            ],
            "description": "Only for `ERROR`: why, as a stable code (see the check's `errorCode`). Null otherwise."
          },
          "errorMessage": {
            "type": [
              "null",
              "string"
            ],
            "description": "Only for `ERROR`: one sentence for the church's staff saying what went wrong and what to do. Null otherwise."
          },
          "timestamp": {
            "type": "string",
            "description": "When this webhook was generated (UTC). Deliveries can arrive out of order; use the latest `timestamp` per check.",
            "format": "date-time"
          }
        },
        "description": "Body of the `check.updated` webhook, POSTed to your endpoint whenever a check changes status."
      },
      "WebhookTestResponse": {
        "examples": [
          {
            "status": "QUEUED"
          }
        ],
        "type": "object",
        "properties": {
          "status": {
            "enum": [
              "QUEUED"
            ],
            "type": "string",
            "description": "Always `QUEUED`: the `ping` event is delivered in the background, with retries."
          }
        },
        "description": "Acknowledgement that a test webhook was queued."
      }
    },
    "securitySchemes": {
      "ApiKey": {
        "type": "apiKey",
        "description": "Your API key (`pk_live_...` in production, `pk_test_...` in test) in the `X-Api-Key` header.",
        "name": "X-Api-Key",
        "in": "header"
      },
      "Bearer": {
        "type": "http",
        "description": "Alternatively, the same API key as a bearer token: `Authorization: Bearer <key>`.",
        "scheme": "bearer",
        "bearerFormat": "API key"
      }
    }
  },
  "security": [
    {
      "ApiKey": [ ]
    },
    {
      "Bearer": [ ]
    }
  ],
  "tags": [
    {
      "name": "Account",
      "description": "Check that your API key works."
    },
    {
      "name": "Settings",
      "description": "Your webhook URL, the return URLs the connect page may send a browser back to, and your webhook signing secret."
    },
    {
      "name": "Connect",
      "description": "The hosted connect page: a church enters its own details and billing on a page we host, so you never see card data or passwords."
    },
    {
      "name": "Clients",
      "description": "Your customers (churches). Each one is a client under your account, addressed by our `clientId` or your own `externalClientId`."
    },
    {
      "name": "Products",
      "description": "The product levels (1 to 6) you can order, and their base prices."
    },
    {
      "name": "Checks",
      "description": "Order (one at a time or in batches), track, cancel and look up background checks for a client's applicants."
    }
  ],
  "webhooks": {
    "check.updated": {
      "post": {
        "summary": "Check status changed",
        "description": "Sent to your webhook URL whenever a check changes status (including each step of a batch and automatic\nresolution of a `PENDING` check), and when you call **Re-send a check's webhook**. The payload carries both\n`status` and the finer `stage`.\n\n**Verify every delivery** before trusting it: compute HMAC-SHA256 over the raw request body (the exact bytes you\nreceived, before parsing), keyed with the UTF-8 bytes of your whole webhook secret (`whsec_` prefix included),\nhex-encode it in lowercase, and compare it to the `X-Partner-Signature` header in constant time.\n\n```js\n// Node.js 20+ (rawBody is a Buffer of the exact request body)\nimport crypto from \"node:crypto\";\nconst expected = crypto.createHmac(\"sha256\", process.env.DATASOURCE_WEBHOOK_SECRET).update(rawBody).digest(\"hex\");\nconst given = Buffer.from(req.get(\"X-Partner-Signature\") ?? \"\", \"utf8\");\nconst valid = given.length === expected.length && crypto.timingSafeEqual(given, Buffer.from(expected, \"utf8\"));\n```\n\n```python\n# Python 3\nimport hashlib, hmac, os\nexpected = hmac.new(os.environ[\"DATASOURCE_WEBHOOK_SECRET\"].encode(\"utf-8\"), raw_body, hashlib.sha256).hexdigest()\nvalid = hmac.compare_digest(expected, request.headers.get(\"X-Partner-Signature\", \"\"))\n```\n\n```csharp\n// C#\nvar expected = Convert.ToHexString(HMACSHA256.HashData(Encoding.UTF8.GetBytes(secret), rawBody)).ToLowerInvariant();\nvar valid = CryptographicOperations.FixedTimeEquals(Encoding.UTF8.GetBytes(expected), Encoding.UTF8.GetBytes(signatureHeader ?? \"\"));\n```\n\n**Delivery.** Respond with any `2xx` within 15 seconds; do slow work asynchronously. Anything else is retried up to\n5 more times with increasing delays. Delivery is at least once and not ordered: drop repeats by `eventId` (the same\non every retry of one event), and keep the state with the latest `timestamp` for each check (key checks by `id`).",
        "operationId": "CheckUpdatedWebhook",
        "parameters": [
          {
            "name": "X-Partner-Signature",
            "in": "header",
            "description": "Lowercase hex HMAC-SHA256 of the raw request body, keyed with the UTF-8 bytes of your whole webhook secret (`whsec_` prefix included).",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/WebhookPayload"
              },
              "example": {
                "event": "check.updated",
                "eventId": "e7a1c3b5-2d4f-4a6b-8c9d-0e1f2a3b4c5d",
                "id": "chk_9c2e4f6a1b3d4e5f8a7b6c5d",
                "externalIdentifier": "vol-8841",
                "clientId": "cl_5b0e6f2a8c1d4e7b9a3f2d6c",
                "status": "CLEAR",
                "stage": "complete",
                "level": 2,
                "expirationDate": "2028-10-18T18:22:41Z",
                "completedAt": "2026-10-18T18:22:41Z",
                "timestamp": "2026-10-18T18:22:42Z"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "Any 2xx acknowledges receipt."
          },
          "default": {
            "description": "Anything else, a timeout (15 seconds) or a connection error is retried, up to 5 more times with increasing delays. Redirects are not followed."
          }
        }
      }
    },
    "batch.completed": {
      "post": {
        "summary": "Batch completed",
        "description": "Sent once when no check of a batch is `QUEUED` or `PENDING` any more: every check has been submitted (or released). It is not a results event; results come as `check.updated`. Same signature, retry and `eventId` rules as `check.updated`.",
        "operationId": "BatchCompletedWebhook",
        "parameters": [
          {
            "name": "X-Partner-Signature",
            "in": "header",
            "description": "Lowercase hex HMAC-SHA256 of the raw request body, keyed with the UTF-8 bytes of your whole webhook secret (`whsec_` prefix included).",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/BatchCompletedWebhook"
              },
              "example": {
                "event": "batch.completed",
                "eventId": "0b8f2e6d-4c1a-4f3e-9d7b-5a2c8e1f6d30",
                "timestamp": "2026-10-16T15:12:30Z",
                "batchId": "3f1c0b9e-7a52-4a6e-9d0c-5e7d2b1a8c44",
                "batchReference": "onboarding-2026-10-16",
                "clientId": "cl_5b0e6f2a8c1d4e7b9a3f2d6c",
                "externalClientId": "org-1042",
                "counts": {
                  "QUEUED": 0,
                  "PENDING": 0,
                  "CREATED": 1
                }
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "Any 2xx acknowledges receipt."
          },
          "default": {
            "description": "Anything else, a timeout (15 seconds) or a connection error is retried, up to 5 more times with increasing delays. Redirects are not followed."
          }
        }
      }
    },
    "client.connected": {
      "post": {
        "summary": "Church connected",
        "description": "Sent when a church finishes the hosted connect page and its client exists (`status` is `Active`). Confirm a connection with this event or `GET /connect/sessions/{sessionId}` before treating the church as connected; never with the browser redirect alone. Same signature, retry and `eventId` rules as `check.updated`.",
        "operationId": "ClientConnectedWebhook",
        "parameters": [
          {
            "name": "X-Partner-Signature",
            "in": "header",
            "description": "Lowercase hex HMAC-SHA256 of the raw request body, keyed with the UTF-8 bytes of your whole webhook secret (`whsec_` prefix included).",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ClientConnectedWebhook"
              },
              "example": {
                "event": "client.connected",
                "eventId": "6c3d9a1e-8b2f-4e5d-a7c0-1f9e3b5d7a24",
                "timestamp": "2026-10-16T15:16:40Z",
                "sessionId": "8d2f6c14-3b7a-4e0e-9c55-1a9b7d3e2f60",
                "externalClientId": "org-1042",
                "clientId": "cl_5b0e6f2a8c1d4e7b9a3f2d6c",
                "status": "Active"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "Any 2xx acknowledges receipt."
          },
          "default": {
            "description": "Anything else, a timeout (15 seconds) or a connection error is retried, up to 5 more times with increasing delays. Redirects are not followed."
          }
        }
      }
    },
    "client.migrated": {
      "post": {
        "summary": "Church migrated",
        "description": "Sent when an existing Datasource church has been moved under your account and its past checks were imported (`importedChecks`). Same signature, retry and `eventId` rules as `check.updated`.",
        "operationId": "ClientMigratedWebhook",
        "parameters": [
          {
            "name": "X-Partner-Signature",
            "in": "header",
            "description": "Lowercase hex HMAC-SHA256 of the raw request body, keyed with the UTF-8 bytes of your whole webhook secret (`whsec_` prefix included).",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ClientMigratedWebhook"
              },
              "example": {
                "event": "client.migrated",
                "eventId": "a4e2c8f0-3d6b-4a1e-b9c5-7d0f2e4a6c81",
                "timestamp": "2026-10-20T14:02:11Z",
                "externalClientId": "org-1042",
                "clientId": "cl_5b0e6f2a8c1d4e7b9a3f2d6c",
                "importedChecks": 214
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "Any 2xx acknowledges receipt."
          },
          "default": {
            "description": "Anything else, a timeout (15 seconds) or a connection error is retried, up to 5 more times with increasing delays. Redirects are not followed."
          }
        }
      }
    },
    "check.expiring": {
      "post": {
        "summary": "Check expiring soon",
        "description": "Sent 30 days and again 7 days before a `CLEAR` or `REVIEW` check's `expirationDate` (each once; a check found later than 30 days out gets only the 7-day one). Use it to prompt the church to re-screen before the clearance lapses. Same signature, retry and `eventId` rules as `check.updated`.",
        "operationId": "CheckExpiringWebhook",
        "parameters": [
          {
            "name": "X-Partner-Signature",
            "in": "header",
            "description": "Lowercase hex HMAC-SHA256 of the raw request body, keyed with the UTF-8 bytes of your whole webhook secret (`whsec_` prefix included).",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CheckExpiringWebhook"
              },
              "example": {
                "event": "check.expiring",
                "eventId": "a4c6e8f0-2b4d-4f6a-8c0e-1a3c5e7f9b2d",
                "timestamp": "2028-09-18T14:00:03Z",
                "id": "chk_9c2e4f6a1b3d4e5f8a7b6c5d",
                "externalIdentifier": "vol-8841",
                "clientId": "cl_5b0e6f2a8c1d4e7b9a3f2d6c",
                "status": "CLEAR",
                "expirationDate": "2028-10-18T18:22:41Z",
                "daysLeft": 30
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "Any 2xx acknowledges receipt."
          },
          "default": {
            "description": "Anything else, a timeout (15 seconds) or a connection error is retried, up to 5 more times with increasing delays. Redirects are not followed."
          }
        }
      }
    },
    "ping": {
      "post": {
        "summary": "Test ping",
        "description": "Sent by `POST /settings/webhook-test` so you can verify your endpoint and your signature check. Same signature, retry and `eventId` rules as `check.updated`.",
        "operationId": "PingWebhook",
        "parameters": [
          {
            "name": "X-Partner-Signature",
            "in": "header",
            "description": "Lowercase hex HMAC-SHA256 of the raw request body, keyed with the UTF-8 bytes of your whole webhook secret (`whsec_` prefix included).",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/PingWebhook"
              },
              "example": {
                "event": "ping",
                "eventId": "f1d3b5a7-9c2e-4d6f-8a0b-3c5e7f9a1b2d",
                "timestamp": "2026-10-16T15:00:00Z"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "Any 2xx acknowledges receipt."
          },
          "default": {
            "description": "Anything else, a timeout (15 seconds) or a connection error is retried, up to 5 more times with increasing delays. Redirects are not followed."
          }
        }
      }
    }
  }
}