{
  "openapi": "3.1.0",
  "info": {
    "title": "Propper Click Accept API",
    "version": "1.0",
    "description": "The Propper Click Accept API records consent acceptances with idempotent writes.\nWhen an end user agrees to a clickwrap or consent flow, your backend posts the acceptance to this API,\nwhich stores the record along with a cryptographically signed evidence bundle.\n\n---\n\n## Base URL\n\n`https://api.propper.ai/v1/click/accept`\n\n## Authentication\n\nOAuth 2.0 Bearer token:\n\n```\nAuthorization: Bearer <access_token>\n```\n\n## OAuth Scopes\n\n- `click:accept` — Record acceptances, with no access to read or change Click data. The scope a customer backend needs.\n- `click:write` and `click:admin` — also record acceptances\n- `click:read` — Read-only; cannot record an acceptance\n\n## Idempotency\n\nEvery acceptance carries an `X-Idempotency-Key`. A retry with the same key and the same body returns the original receipt instead of recording a second acceptance, with no time limit. A key reused for a different body is refused with 422 `CLICK_IDEMPOTENCY_KEY_REUSED`.\n\n## Rate limits\n\nRequests are limited per OAuth client. Over the limit, the API answers 429 `CLICK_RATE_LIMITED` with a `Retry-After` header (seconds); every limited response carries `X-RateLimit-Limit`, `X-RateLimit-Remaining` and `X-RateLimit-Reset`.\n\n## Key Endpoints\n\n- **Acceptance**: Record and query consent acceptances\n\n---\n\n## Request correlation\n\nEvery response (success and error) includes an `x-request-id` header (a UUID) and a `traceparent` header (W3C Trace Context). Error bodies also carry the same id as `requestId`. Quote `x-request-id` when contacting support to pinpoint the exact request, and use it to correlate the API with your own logs. Send your own `traceparent` to tie a multi-call workflow under one trace.\n\n---\n\n## Errors\n\nEvery error response has the same JSON body:\n\n```json\n{\n  \"errorCode\": \"CLICK_VALIDATION_FAILED\",\n  \"message\": \"Request validation failed\",\n  \"requestId\": \"0b6f3a52-9a44-4f4b-9c1e-6a1f0e0f7c11\",\n  \"details\": [\n    { \"field\": \"name\", \"message\": \"name must be a string\", \"code\": \"isString\" }\n  ]\n}\n```\n\n- `errorCode` is a stable code you can branch on. The HTTP status says the class of failure; the code says which one. New codes may be added, so treat an unknown code like the default code for its status.\n- `message` is a short explanation for a person. Do not parse it.\n- `requestId` is the same value as the `x-request-id` response header.\n- `details` appears only on validation failures (400 `CLICK_VALIDATION_FAILED`, and a 422 that names the field it rejects). Each entry names the `field`, what is wrong in `message`, and the rule in `code`. A server error (5xx) never carries `details`, and its `message` is generic.\n- `requiredEntitlement` appears only with 403 `ENTITLEMENT_REQUIRED` and names the entitlement your account needs, such as `click.access`.\n\n### Error codes\n\n| Code | Status | Meaning |\n| --- | --- | --- |\n| `CLICK_BAD_REQUEST` | 400 | The request is malformed or cannot be processed as sent. |\n| `CLICK_VALIDATION_FAILED` | 400 | A field of the request is invalid. `details` names each field and the rule it broke. |\n| `CLICK_UNAUTHORIZED` | 401 | The credentials are missing, invalid or expired. |\n| `CLICK_FORBIDDEN` | 403 | The credentials are valid but the token lacks the required scope, or the resource belongs to another organization. |\n| `CLICK_NOT_FOUND` | 404 | The resource does not exist in your organization. |\n| `CLICK_CONFLICT` | 409 | The request conflicts with the current state of a resource, such as a duplicate. |\n| `CLICK_UNPROCESSABLE` | 422 | The request is well formed but cannot be applied. |\n| `CLICK_RATE_LIMITED` | 429 | Too many requests. Retry after a pause. |\n| `CLICK_INTERNAL_ERROR` | 500 | An unexpected error on our side. Quote the `requestId` when contacting support. |\n| `CLICK_SERVICE_UNAVAILABLE` | 503 | The service is temporarily unavailable. Retry with backoff. |\n| `CLICK_DEPLOYMENT_NOT_FOUND` | 404 | No deployment with this ID exists in your organization. |\n| `CLICK_DEPLOYMENT_INACTIVE` | 404 | The deployment exists but is not active, so it serves nothing. |\n| `CLICK_CONTENT_MISSING` | 500 | The deployment has no content to serve. |\n| `CLICK_CONTENT_UNAVAILABLE` | 503 | The deployment's content is temporarily unavailable. Retry with backoff. |\n| `CLICK_RENDER_UPSTREAM_UNAVAILABLE` | 503 | Rendering is temporarily unavailable. Retry with backoff. |\n| `CLICK_ACCEPT_UPSTREAM_UNAVAILABLE` | 503 | Recording the acceptance is temporarily unavailable. Nothing was recorded. Retry with backoff. |\n| `CLICK_ACCEPT_OUTCOME_UNKNOWN` | 504 | The accept call timed out or dropped after the request may have reached the recorder, so the acceptance may or may not be recorded. Retry with the same `X-Idempotency-Key`: the answer is the receipt if it was recorded, or a fresh attempt if not. |\n| `CLICK_DASHBOARD_UPSTREAM_UNAVAILABLE` | 503 | The dashboard metrics are temporarily unavailable. Retry with backoff. |\n| `CLICK_CHECKSUM_MISMATCH` | 422 | The checksum sent with an acceptance is not the checksum of the content the deployment serves. Nothing is recorded. |\n| `CLICK_VERSION_NOT_SERVED` | 422 | The version named by an acceptance is not the version the deployment serves, nor one it served in the last 15 minutes. Nothing is recorded. |\n| `CLICK_DEPLOYMENT_HAS_REVISIONS` | 409 | A deployment with revisions is history that evidence points at: deactivate it instead of deleting it. |\n| `CLICK_NOTHING_CHANGED` | 409 | The draft equals the latest published version, so there is nothing to publish. |\n| `CLICK_VERSION_CONFLICT` | 409 | Another version was published while this one was being prepared. Review the new version and publish again. |\n| `CLICK_TEMPLATE_ARCHIVED` | 409 | The template is archived, so it cannot be published, edited or deployed. |\n| `CLICK_FIRST_PUBLISH_MUST_BE_MAJOR` | 422 | The first publish of a template is version 1.0.0 and must be a MAJOR. |\n| `CLICK_CONSPICUOUSNESS_ACK_REQUIRED` | 422 | The presentation changes were flagged, and publishing them as a PATCH needs a conspicuousness acknowledgment. |\n| `CLICK_REACCEPT_GATE_REQUIRED` | 422 | A MAJOR that follows an earlier version needs a re-acceptance gate: IMMEDIATE, BY_DEADLINE (with a future deadline) or NEXT_VISIT. |\n| `CLICK_GATE_NOT_APPLICABLE` | 422 | A re-acceptance gate, deadline or affected set applies only to a MAJOR that follows an earlier version. |\n| `CLICK_LINKED_DOCUMENT_UNREACHABLE` | 422 | A document linked from the template could not be fetched and hashed, so the version was not published. |\n| `CLICK_TEMPLATE_NOT_PUBLISHABLE` | 422 | The draft cannot be published, for example because it has no content in any locale. |\n| `CLICK_CONTENT_UNSAFE` | 422 | A body contains markup that would have to be removed, such as a script, an event handler or a tag outside the allowed set. Nothing was written; the message names what would be removed. |\n| `CLICK_DEPLOYMENT_SLOT_TAKEN` | 409 | A deployment already exists for this environment, domain, path and variant. |\n| `CLICK_REVISION_CONFLICT` | 409 | The deployment changed while this request was being applied. Read it again and retry. |\n| `CLICK_NOTHING_TO_CHANGE` | 409 | The deployment already serves that version, or already has those settings. |\n| `CLICK_VERSION_TEMPLATE_MISMATCH` | 409 | The version belongs to a different template than the deployment. |\n| `CLICK_IDEMPOTENCY_KEY_REUSED` | 422 | The `X-Idempotency-Key` was already used for a different request body. Nothing is recorded. |\n| `CLICK_DEPLOYMENT_ENVIRONMENT_LOCKED` | 409 | The deployment has recorded acceptances, so its environment cannot change. Create a new deployment for the other environment. |\n| `CLICK_RETURN_URL_INVALID` | 400 | The return URL is not allowed: it must use `https`, carry no credentials and be an allowed origin for the deployment. `details` names the reason. |\n| `CLICK_HOSTED_TYPE_UNSUPPORTED` | 422 | The hosted acceptance page serves static agreements only; this deployment's template is of another type. |\n| `CLICK_ACCEPTANCE_LINK_NOT_FOUND` | 404 | No acceptance link with this ID exists in your organization. |\n| `CLICK_ACCEPTANCE_LINK_COMPLETED` | 409 | The signer already accepted or declined through this link, so it can no longer be revoked. |\n| `CLICK_ACCEPTANCE_LINK_MINT_CONFLICT` | 409 | Another mint for the same deployment and signer is in progress. Retry the request. |\n| `ENTITLEMENT_REQUIRED` | 403 | Your account does not include Click. `requiredEntitlement` names the entitlement to add. This is the platform code every Propper API returns for a missing entitlement, so it has no `CLICK_` prefix. |",
    "contact": {
      "name": "Propper Support",
      "url": "https://propper.ai",
      "email": "support@propper.ai"
    }
  },
  "tags": [
    {
      "name": "Acceptance",
      "description": "Record and query consent acceptances"
    }
  ],
  "servers": [
    {
      "url": "https://api.propper.ai",
      "description": "Production server"
    }
  ],
  "security": [
    {
      "bearer": []
    }
  ],
  "components": {
    "securitySchemes": {
      "bearer": {
        "scheme": "bearer",
        "bearerFormat": "JWT",
        "type": "http",
        "description": "OAuth 2.0 Bearer token"
      }
    },
    "headers": {
      "X-Request-Id": {
        "schema": {
          "type": "string",
          "format": "uuid"
        },
        "description": "Correlation id for this request, returned on every response (success and error). Quote it when contacting support to pinpoint the exact request, and use it to correlate your own logs. It is the same 128-bit id as the trace-id segment of the `traceparent` header."
      },
      "traceparent": {
        "schema": {
          "type": "string"
        },
        "description": "W3C Trace Context identifier for this request, returned on every response. Send your own `traceparent` on a request to have it adopted, so a multi-call workflow shares one trace. Format: `00-<32-hex trace-id>-<16-hex span-id>-<2-hex flags>`."
      }
    },
    "parameters": {
      "IdempotencyKeyRequired": {
        "name": "X-Idempotency-Key",
        "in": "header",
        "required": true,
        "description": "Required. Makes a retry safe. Send the same key on every attempt of one acceptance and it is recorded once: a retry with the same key and the same body returns the original receipt, with no time limit, and records nothing new. Use 8 to 128 letters, digits, dashes or underscores; a UUID is a good choice. The body is compared: a key reused for a different body is refused with 422 `CLICK_IDEMPOTENCY_KEY_REUSED` and nothing is recorded, so use a new key for every distinct acceptance.",
        "schema": {
          "type": "string",
          "minLength": 8,
          "maxLength": 128,
          "pattern": "^[a-zA-Z0-9_-]{8,128}$"
        }
      }
    },
    "responses": {
      "BadRequest": {
        "description": "The request is malformed or a field is invalid (`CLICK_BAD_REQUEST`, or `CLICK_VALIDATION_FAILED` with `details`).",
        "headers": {
          "X-Request-Id": {
            "$ref": "#/components/headers/X-Request-Id"
          },
          "traceparent": {
            "$ref": "#/components/headers/traceparent"
          }
        },
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/ErrorResponse"
            },
            "example": {
              "errorCode": "CLICK_VALIDATION_FAILED",
              "message": "Request validation failed",
              "requestId": "0b6f3a52-9a44-4f4b-9c1e-6a1f0e0f7c11",
              "details": [
                {
                  "field": "name",
                  "message": "name must be a string",
                  "code": "isString"
                }
              ]
            }
          }
        }
      },
      "Unauthorized": {
        "description": "The credentials are missing, invalid or expired (`CLICK_UNAUTHORIZED`).",
        "headers": {
          "X-Request-Id": {
            "$ref": "#/components/headers/X-Request-Id"
          },
          "traceparent": {
            "$ref": "#/components/headers/traceparent"
          }
        },
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/ErrorResponse"
            },
            "example": {
              "errorCode": "CLICK_UNAUTHORIZED",
              "message": "Invalid or expired token"
            }
          }
        }
      },
      "Forbidden": {
        "description": "The token lacks the scope this operation needs, or the resource belongs to another organization (`CLICK_FORBIDDEN`), or your account does not include Click (`ENTITLEMENT_REQUIRED`, with `requiredEntitlement`).",
        "headers": {
          "X-Request-Id": {
            "$ref": "#/components/headers/X-Request-Id"
          },
          "traceparent": {
            "$ref": "#/components/headers/traceparent"
          }
        },
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/ErrorResponse"
            },
            "example": {
              "errorCode": "CLICK_FORBIDDEN",
              "message": "Insufficient scope"
            }
          }
        }
      },
      "NotFound": {
        "description": "The resource does not exist in your organization (`CLICK_NOT_FOUND`, or a more specific code).",
        "headers": {
          "X-Request-Id": {
            "$ref": "#/components/headers/X-Request-Id"
          },
          "traceparent": {
            "$ref": "#/components/headers/traceparent"
          }
        },
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/ErrorResponse"
            },
            "example": {
              "errorCode": "CLICK_NOT_FOUND",
              "message": "Resource not found"
            }
          }
        }
      },
      "Conflict": {
        "description": "The request conflicts with the current state of a resource (`CLICK_CONFLICT`, or a more specific code).",
        "headers": {
          "X-Request-Id": {
            "$ref": "#/components/headers/X-Request-Id"
          },
          "traceparent": {
            "$ref": "#/components/headers/traceparent"
          }
        },
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/ErrorResponse"
            },
            "example": {
              "errorCode": "CLICK_CONFLICT",
              "message": "A resource with these values already exists"
            }
          }
        }
      },
      "UnprocessableEntity": {
        "description": "The request is well formed but cannot be applied (`CLICK_UNPROCESSABLE`, or a more specific code).",
        "headers": {
          "X-Request-Id": {
            "$ref": "#/components/headers/X-Request-Id"
          },
          "traceparent": {
            "$ref": "#/components/headers/traceparent"
          }
        },
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/ErrorResponse"
            },
            "example": {
              "errorCode": "CLICK_UNPROCESSABLE",
              "message": "The request cannot be applied"
            }
          }
        }
      },
      "InternalError": {
        "description": "An unexpected error on our side (`CLICK_INTERNAL_ERROR`). The body is generic; quote the `requestId` when contacting support.",
        "headers": {
          "X-Request-Id": {
            "$ref": "#/components/headers/X-Request-Id"
          },
          "traceparent": {
            "$ref": "#/components/headers/traceparent"
          }
        },
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/ErrorResponse"
            },
            "example": {
              "errorCode": "CLICK_INTERNAL_ERROR",
              "message": "Internal server error"
            }
          }
        }
      },
      "ServiceUnavailable": {
        "description": "A dependency is temporarily unavailable (`CLICK_SERVICE_UNAVAILABLE`, or a more specific code). Retry with backoff.",
        "headers": {
          "X-Request-Id": {
            "$ref": "#/components/headers/X-Request-Id"
          },
          "traceparent": {
            "$ref": "#/components/headers/traceparent"
          }
        },
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/ErrorResponse"
            },
            "example": {
              "errorCode": "CLICK_SERVICE_UNAVAILABLE",
              "message": "The service is temporarily unavailable"
            }
          }
        }
      }
    },
    "schemas": {
      "ErrorFieldDetail": {
        "type": "object",
        "description": "One field-level cause of a rejected request.",
        "properties": {
          "field": {
            "type": "string",
            "description": "Path of the request property, such as `name` or `items.0.name`.",
            "example": "name"
          },
          "message": {
            "type": "string",
            "description": "What is wrong with that property.",
            "example": "name must be a string"
          },
          "code": {
            "type": "string",
            "description": "The validation rule that failed.",
            "example": "isString"
          }
        }
      },
      "ErrorResponse": {
        "type": "object",
        "description": "The body of every error response.",
        "properties": {
          "errorCode": {
            "type": "string",
            "description": "Stable, machine-readable error code. One of: `CLICK_BAD_REQUEST`, `CLICK_VALIDATION_FAILED`, `CLICK_UNAUTHORIZED`, `CLICK_FORBIDDEN`, `CLICK_NOT_FOUND`, `CLICK_CONFLICT`, `CLICK_UNPROCESSABLE`, `CLICK_RATE_LIMITED`, `CLICK_INTERNAL_ERROR`, `CLICK_SERVICE_UNAVAILABLE`, `CLICK_DEPLOYMENT_NOT_FOUND`, `CLICK_DEPLOYMENT_INACTIVE`, `CLICK_CONTENT_MISSING`, `CLICK_CONTENT_UNAVAILABLE`, `CLICK_RENDER_UPSTREAM_UNAVAILABLE`, `CLICK_ACCEPT_UPSTREAM_UNAVAILABLE`, `CLICK_ACCEPT_OUTCOME_UNKNOWN`, `CLICK_DASHBOARD_UPSTREAM_UNAVAILABLE`, `CLICK_CHECKSUM_MISMATCH`, `CLICK_VERSION_NOT_SERVED`, `CLICK_DEPLOYMENT_HAS_REVISIONS`, `CLICK_NOTHING_CHANGED`, `CLICK_VERSION_CONFLICT`, `CLICK_TEMPLATE_ARCHIVED`, `CLICK_FIRST_PUBLISH_MUST_BE_MAJOR`, `CLICK_CONSPICUOUSNESS_ACK_REQUIRED`, `CLICK_REACCEPT_GATE_REQUIRED`, `CLICK_GATE_NOT_APPLICABLE`, `CLICK_LINKED_DOCUMENT_UNREACHABLE`, `CLICK_TEMPLATE_NOT_PUBLISHABLE`, `CLICK_CONTENT_UNSAFE`, `CLICK_DEPLOYMENT_SLOT_TAKEN`, `CLICK_REVISION_CONFLICT`, `CLICK_NOTHING_TO_CHANGE`, `CLICK_VERSION_TEMPLATE_MISMATCH`, `CLICK_IDEMPOTENCY_KEY_REUSED`, `CLICK_DEPLOYMENT_ENVIRONMENT_LOCKED`, `CLICK_RETURN_URL_INVALID`, `CLICK_HOSTED_TYPE_UNSUPPORTED`, `CLICK_ACCEPTANCE_LINK_NOT_FOUND`, `CLICK_ACCEPTANCE_LINK_COMPLETED`, `CLICK_ACCEPTANCE_LINK_MINT_CONFLICT`, `ENTITLEMENT_REQUIRED`. New codes may be added.",
            "example": "CLICK_VALIDATION_FAILED"
          },
          "message": {
            "type": "string",
            "description": "Short explanation for a person. Do not parse it.",
            "example": "Request validation failed"
          },
          "requestId": {
            "type": "string",
            "description": "Correlation id of the request, the same value as the `x-request-id` response header. Quote it when contacting support.",
            "example": "0b6f3a52-9a44-4f4b-9c1e-6a1f0e0f7c11"
          },
          "details": {
            "type": "array",
            "description": "Field-level causes. Present on validation failures only, never on a server error.",
            "items": {
              "$ref": "#/components/schemas/ErrorFieldDetail"
            }
          },
          "requiredEntitlement": {
            "type": "string",
            "description": "The entitlement your account needs. Present with 403 `ENTITLEMENT_REQUIRED` only.",
            "example": "click.access"
          }
        },
        "required": [
          "errorCode",
          "message"
        ]
      },
      "IdentityAssertionDto": {
        "type": "object",
        "properties": {
          "level": {
            "type": "string",
            "description": "Identity assertion level/method",
            "example": "email"
          },
          "method": {
            "type": "string",
            "description": "Identity assertion method",
            "enum": [
              "otp",
              "sso",
              "webauthn",
              "email",
              "simple"
            ],
            "example": "email"
          },
          "value": {
            "type": "string",
            "description": "Raw assertion data to be hashed (will not be stored)",
            "example": "e2e-test-recipient+click-docs@propper.ai:verification-code-123"
          },
          "email": {
            "type": "string",
            "description": "User email",
            "example": "e2e-test-recipient+click-docs@propper.ai"
          },
          "name": {
            "type": "string",
            "description": "User name",
            "example": "John Doe"
          },
          "company": {
            "type": "string",
            "description": "User company",
            "example": "Acme Corp"
          },
          "verifiedAt": {
            "type": "number",
            "description": "Verification timestamp",
            "example": 1234567890
          },
          "sessionId": {
            "type": "string",
            "description": "Verification session ID for OTP/2FA methods",
            "example": "550e8400-e29b-41d4-a716-446655440003"
          },
          "verificationToken": {
            "type": "string",
            "description": "Verification token proving identity was verified",
            "example": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
          }
        }
      },
      "AcceptConsentDto": {
        "type": "object",
        "properties": {
          "templateVersionId": {
            "type": "string",
            "description": "Template version UUID",
            "example": "550e8400-e29b-41d4-a716-446655440000"
          },
          "deploymentId": {
            "type": "string",
            "description": "Deployment ID that served the consent form",
            "example": "550e8400-e29b-41d4-a716-446655440002"
          },
          "checksum": {
            "type": "string",
            "description": "SHA-256 (hex) of the `content` the deployment served, exactly as the render API returned it (its `checksum`). Must match what the deployment serves for the locale in `metadata.locale`, which must be the locale it was rendered with (omitted, the default locale is used, with the same fallback as render), or the acceptance is rejected with 422 `CLICK_CHECKSUM_MISMATCH`. The field is required: a body without it is a 400 validation error, while an empty or non-matching value is a 422.",
            "example": "a665a45920422f9d417e4867efdc4fb8a04a1f3fff1fa07e998e86f7f7a27ae3"
          },
          "consentMethod": {
            "type": "string",
            "description": "Consent method used for acceptance",
            "enum": [
              "BUTTON",
              "CHECKBOX",
              "SCROLL",
              "SIGNIN_WRAP"
            ],
            "example": "BUTTON"
          },
          "sessionId": {
            "type": "string",
            "description": "Optional session ID",
            "example": "550e8400-e29b-41d4-a716-446655440001"
          },
          "userId": {
            "type": "string",
            "description": "Optional user ID",
            "example": "user123"
          },
          "email": {
            "type": "string",
            "description": "Optional email address",
            "example": "e2e-test-recipient+click-docs@propper.ai"
          },
          "identityAssertion": {
            "description": "Identity assertion for proof of acceptance",
            "allOf": [
              {
                "$ref": "#/components/schemas/IdentityAssertionDto"
              }
            ]
          },
          "identityAssertions": {
            "description": "Identity assertions array for proof of acceptance",
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/IdentityAssertionDto"
            }
          },
          "variant": {
            "type": "string",
            "description": "Optional variant identifier",
            "example": "A"
          },
          "capturedHtml": {
            "type": "string",
            "description": "Captured HTML of the page the content was shown on, for evidence generation",
            "example": "<div class=\"propper-modal\">...</div>"
          },
          "templateSnapshot": {
            "type": "object",
            "description": "Template snapshot (HTML + CSS) for screenshot generation",
            "example": {
              "html": "<div>...</div>",
              "css": ".modal { ... }"
            }
          },
          "browserInfo": {
            "type": "object",
            "description": "Browser information for screenshot generation",
            "example": {
              "userAgent": "Mozilla/5.0...",
              "viewport": {
                "width": 1920,
                "height": 1080
              }
            }
          },
          "metadata": {
            "type": "object",
            "description": "Additional metadata. `locale` must be the locale the content was rendered with (the `locale` the render API returned); when it is omitted the checksum is checked against the default locale (en-US, then en, then the first locale the deployment has), so a checksum rendered for another locale is rejected with 422 `CLICK_CHECKSUM_MISMATCH`. Also accepts optional container dimensions of the page the content was shown in.",
            "example": {
              "locale": "en-US",
              "source": "mobile-app",
              "version": "1.2.3",
              "containerDimensions": {
                "width": 800,
                "height": 600
              },
              "verificationSessionId": "550e8400-e29b-41d4-a716-446655440003",
              "verificationToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
            }
          }
        },
        "required": [
          "templateVersionId",
          "deploymentId",
          "checksum",
          "consentMethod"
        ]
      },
      "AcceptConsentResponseDto": {
        "type": "object",
        "properties": {
          "receiptId": {
            "type": "string",
            "description": "Receipt ID for the acceptance",
            "example": "550e8400-e29b-41d4-a716-446655440002"
          },
          "acceptedAt": {
            "type": "string",
            "description": "Timestamp of acceptance in ISO 8601 format",
            "example": "2024-01-15T10:30:00.000Z"
          },
          "message": {
            "type": "string",
            "description": "Success message",
            "example": "Acceptance recorded successfully"
          },
          "evidenceStatus": {
            "type": "string",
            "enum": [
              "pending",
              "complete",
              "failed"
            ],
            "description": "State of the evidence for this acceptance. Evidence is produced asynchronously, so a new acceptance is pending; read the receipt for complete or failed.",
            "example": "pending"
          }
        },
        "required": [
          "receiptId",
          "acceptedAt",
          "message",
          "evidenceStatus"
        ]
      }
    }
  },
  "paths": {
    "/v1/click/accept": {
      "post": {
        "operationId": "acceptConsent",
        "summary": "Accept consent",
        "description": "Record consent acceptance with idempotent writes and Pub/Sub integration",
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKeyRequired"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/AcceptConsentDto"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Consent acceptance recorded successfully",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AcceptConsentResponseDto"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "traceparent": {
                "$ref": "#/components/headers/traceparent"
              }
            }
          },
          "400": {
            "description": "Invalid request data, including a body with no `checksum` field (an empty or wrong checksum is a 422 instead), a missing or malformed `X-Idempotency-Key`, or an identity method the template does not allow (`CLICK_BAD_REQUEST`, `CLICK_VALIDATION_FAILED`)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "traceparent": {
                "$ref": "#/components/headers/traceparent"
              }
            }
          },
          "401": {
            "description": "Missing or invalid authentication credentials. An acceptance needs an OAuth Bearer token.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "traceparent": {
                "$ref": "#/components/headers/traceparent"
              }
            }
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "description": "No deployment with this ID in your organization (`CLICK_DEPLOYMENT_NOT_FOUND`), or it is not active (`CLICK_DEPLOYMENT_INACTIVE`)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "traceparent": {
                "$ref": "#/components/headers/traceparent"
              }
            }
          },
          "422": {
            "description": "The checksum is empty or is not the SHA-256 of the content the deployment serves, code `CLICK_CHECKSUM_MISMATCH`: the details name the version served, never the expected checksum. Or the X-Idempotency-Key was already used for a different request body, code `CLICK_IDEMPOTENCY_KEY_REUSED`. Nothing is recorded.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "traceparent": {
                "$ref": "#/components/headers/traceparent"
              }
            }
          },
          "429": {
            "description": "The OAuth client exceeded its request rate. Nothing is recorded. Retry after the number of seconds in the Retry-After header.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "traceparent": {
                "$ref": "#/components/headers/traceparent"
              },
              "Retry-After": {
                "schema": {
                  "type": "integer"
                },
                "description": "Seconds until the rate-limit window resets"
              },
              "X-RateLimit-Limit": {
                "schema": {
                  "type": "integer"
                },
                "description": "Requests allowed in the current window"
              },
              "X-RateLimit-Remaining": {
                "schema": {
                  "type": "integer"
                },
                "description": "Requests left in the current window"
              },
              "X-RateLimit-Reset": {
                "schema": {
                  "type": "integer"
                },
                "description": "Unix time, in seconds, at which the window resets"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "500": {
            "description": "The deployment has no content to serve (`CLICK_CONTENT_MISSING`)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "traceparent": {
                "$ref": "#/components/headers/traceparent"
              }
            }
          },
          "503": {
            "description": "Content storage (`CLICK_CONTENT_UNAVAILABLE`) or the acceptance backend (`CLICK_ACCEPT_UPSTREAM_UNAVAILABLE`) is temporarily unavailable. The request never reached the recorder, so nothing is recorded; retry with the same `X-Idempotency-Key`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "traceparent": {
                "$ref": "#/components/headers/traceparent"
              }
            }
          },
          "504": {
            "description": "The call to the acceptance backend timed out or dropped after the request may have reached it (`CLICK_ACCEPT_OUTCOME_UNKNOWN`). The acceptance may or may not be recorded. Retry with the same `X-Idempotency-Key`: the answer is the receipt if it was recorded, or a fresh attempt if not. Do not tell your user the acceptance failed until the retry answers.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "traceparent": {
                "$ref": "#/components/headers/traceparent"
              }
            }
          }
        },
        "tags": [
          "Acceptance"
        ],
        "security": [
          {
            "bearer": [
              "click:accept"
            ]
          }
        ]
      }
    }
  }
}