# Payerworks API client for Python 3.9 and later: the standard library only.
#
# Generated from the Payerworks API route list (142 routes, build f7d547c3); do not edit it. Download it
# again when the API adds a route: https://payerworks.com/sdk/payerworks.py
# Payerworks builds, checks and reads files and explains them; it transmits nothing.
#
# Method names: the HTTP method, then each path segment after /v1 ({param} as by_ and its name), in
# snake_case. POST /v1/claims/validate is post_claims_validate; GET /v1/codes/carc/{code} is
# get_codes_carc_by_code(code).
"""Payerworks API client: one method per route, webhook signature checks, no dependencies."""

from __future__ import annotations

import base64
import binascii
import email.utils
import hashlib
import hmac
import http.client
import json
import os
import re
import socket
import time
import urllib.error
import urllib.parse
import urllib.request
import uuid
from typing import Any, Callable, Dict, Mapping, Optional, Union

__all__ = ["Payerworks", "PayerworksError", "verify_payerworks_signature", "verify_standard_webhook", "ROUTES", "PAYERWORKS_SDK"]

_DEFAULT_BASE_URL = ""

PAYERWORKS_SDK = {"language": "python", "api": "v1", "build": "f7d547c3", "routes": 142}

# Every route of the API, as this file names it.
ROUTES = (
    {"name": "get_health", "method": "GET", "path": "/v1/health", "title": "Health"},
    {"name": "post_claims_validate", "method": "POST", "path": "/v1/claims/validate", "title": "Validate claims"},
    {"name": "post_claims_837p", "method": "POST", "path": "/v1/claims/837p", "title": "Write an 837P"},
    {"name": "post_claims_837i", "method": "POST", "path": "/v1/claims/837i", "title": "Write an 837I"},
    {"name": "post_claims_837i_validate", "method": "POST", "path": "/v1/claims/837i/validate", "title": "Validate institutional claims"},
    {"name": "post_claims_837d", "method": "POST", "path": "/v1/claims/837d", "title": "Write an 837D"},
    {"name": "post_claims_837d_validate", "method": "POST", "path": "/v1/claims/837d/validate", "title": "Validate dental claims"},
    {"name": "post_claims_correction_plan", "method": "POST", "path": "/v1/claims/correction-plan", "title": "Plan a corrected or void claim"},
    {"name": "get_edits", "method": "GET", "path": "/v1/edits", "title": "Every claim edit"},
    {"name": "get_edits_by_code", "method": "GET", "path": "/v1/edits/{code}", "title": "One claim edit"},
    {"name": "post_x12_parse", "method": "POST", "path": "/v1/x12/parse", "title": "Read an X12 file"},
    {"name": "post_claims_track", "method": "POST", "path": "/v1/claims/track", "title": "Track claims"},
    {"name": "post_acknowledgments_explain", "method": "POST", "path": "/v1/acknowledgments/explain", "title": "Explain an acknowledgment"},
    {"name": "post_claims_cms1500", "method": "POST", "path": "/v1/claims/cms1500", "title": "Draw claims on the CMS-1500"},
    {"name": "post_attachments_275", "method": "POST", "path": "/v1/attachments/275", "title": "Write a 275 attachment"},
    {"name": "post_attachments_requests_explain", "method": "POST", "path": "/v1/attachments/requests/explain", "title": "Read a request for more information"},
    {"name": "post_attachments_requests_check", "method": "POST", "path": "/v1/attachments/requests/check", "title": "Check a 275 against its request"},
    {"name": "post_edits_rules_check", "method": "POST", "path": "/v1/edits/rules/check", "title": "Check your own claim rules"},
    {"name": "post_eligibility_270", "method": "POST", "path": "/v1/eligibility/270", "title": "Write a 270"},
    {"name": "post_eligibility_explain", "method": "POST", "path": "/v1/eligibility/explain", "title": "Explain an eligibility answer"},
    {"name": "post_eligibility_identity_retry", "method": "POST", "path": "/v1/eligibility/identity-retry", "title": "Find the patient again"},
    {"name": "post_eligibility_service_type_test", "method": "POST", "path": "/v1/eligibility/service-type-test", "title": "Does a payer answer a service type"},
    {"name": "post_eligibility_coordination", "method": "POST", "path": "/v1/eligibility/coordination", "title": "Which plan pays first"},
    {"name": "post_eligibility_discovery_verify", "method": "POST", "path": "/v1/eligibility/discovery/verify", "title": "Is this coverage the patient’s"},
    {"name": "post_eligibility_pdf", "method": "POST", "path": "/v1/eligibility/pdf", "title": "Print an eligibility answer"},
    {"name": "post_claim_status_276", "method": "POST", "path": "/v1/claim-status/276", "title": "Write a 276"},
    {"name": "post_claim_status_explain", "method": "POST", "path": "/v1/claim-status/explain", "title": "Explain a claim status answer"},
    {"name": "post_claims_follow_up", "method": "POST", "path": "/v1/claims/follow-up", "title": "Follow up on open claims"},
    {"name": "post_remittances_explain", "method": "POST", "path": "/v1/remittances/explain", "title": "Explain a remittance"},
    {"name": "post_denials_next_step", "method": "POST", "path": "/v1/denials/next-step", "title": "The next step on a denial"},
    {"name": "post_appeals_letter", "method": "POST", "path": "/v1/appeals/letter", "title": "Write an appeal letter"},
    {"name": "post_appeals_letter_check", "method": "POST", "path": "/v1/appeals/letter/check", "title": "Check an edited letter"},
    {"name": "post_remittances_pdf", "method": "POST", "path": "/v1/remittances/pdf", "title": "Print a remittance"},
    {"name": "post_appeals_form", "method": "POST", "path": "/v1/appeals/form", "title": "Fill a Medicare appeal form"},
    {"name": "post_remittances_from_image", "method": "POST", "path": "/v1/remittances/from-image", "title": "Read a paper EOB into a draft 835"},
    {"name": "post_remittances_duplicates", "method": "POST", "path": "/v1/remittances/duplicates", "title": "Find duplicate remittances"},
    {"name": "post_remittances_reassociate", "method": "POST", "path": "/v1/remittances/reassociate", "title": "Match deposits to remittances"},
    {"name": "post_remittances_split", "method": "POST", "path": "/v1/remittances/split", "title": "Split an 835 by payee or rendering provider"},
    {"name": "post_remittances_recoupments", "method": "POST", "path": "/v1/remittances/recoupments", "title": "Match recoupments to the claims they take back"},
    {"name": "get_filing_limits", "method": "GET", "path": "/v1/filing-limits", "title": "Published claim filing windows"},
    {"name": "get_filing_limits_by_payer", "method": "GET", "path": "/v1/filing-limits/{payer}", "title": "One payer’s filing window"},
    {"name": "post_analytics_summary", "method": "POST", "path": "/v1/analytics/summary", "title": "Summarize your claims"},
    {"name": "post_remittances_variance", "method": "POST", "path": "/v1/remittances/variance", "title": "Check payments against your fee schedule"},
    {"name": "post_claims_risk", "method": "POST", "path": "/v1/claims/risk", "title": "Check a claim against your past denials"},
    {"name": "post_denials_prioritize", "method": "POST", "path": "/v1/denials/prioritize", "title": "Rank your open denials"},
    {"name": "post_authorizations_278", "method": "POST", "path": "/v1/authorizations/278", "title": "Write a 278"},
    {"name": "post_authorizations_explain", "method": "POST", "path": "/v1/authorizations/explain", "title": "Explain a 278 response"},
    {"name": "post_sandbox_authorizations", "method": "POST", "path": "/v1/sandbox/authorizations", "title": "Run the prior authorization sandbox"},
    {"name": "post_authorizations_required", "method": "POST", "path": "/v1/authorizations/required", "title": "Does Medicare require it"},
    {"name": "post_estimates", "method": "POST", "path": "/v1/estimates", "title": "Estimate a patient’s share"},
    {"name": "post_estimates_good_faith", "method": "POST", "path": "/v1/estimates/good-faith", "title": "Lay out a Good Faith Estimate"},
    {"name": "post_dme_replacement_window", "method": "POST", "path": "/v1/dme/replacement-window", "title": "When equipment may be replaced"},
    {"name": "post_financial_assistance_fpl", "method": "POST", "path": "/v1/financial-assistance/fpl", "title": "Income against the poverty guideline"},
    {"name": "post_price_transparency_validate", "method": "POST", "path": "/v1/price-transparency/validate", "title": "Check a hospital standard charges file"},
    {"name": "post_providers", "method": "POST", "path": "/v1/providers", "title": "Create a provider"},
    {"name": "get_providers", "method": "GET", "path": "/v1/providers", "title": "List your providers"},
    {"name": "get_providers_by_id", "method": "GET", "path": "/v1/providers/{id}", "title": "One provider"},
    {"name": "post_providers_by_id", "method": "POST", "path": "/v1/providers/{id}", "title": "Change a provider"},
    {"name": "delete_providers_by_id", "method": "DELETE", "path": "/v1/providers/{id}", "title": "Delete a provider"},
    {"name": "post_enrollments", "method": "POST", "path": "/v1/enrollments", "title": "Request an enrollment"},
    {"name": "get_enrollments", "method": "GET", "path": "/v1/enrollments", "title": "List your enrollments"},
    {"name": "get_enrollments_by_id", "method": "GET", "path": "/v1/enrollments/{id}", "title": "One enrollment"},
    {"name": "post_enrollments_by_id", "method": "POST", "path": "/v1/enrollments/{id}", "title": "Change an enrollment"},
    {"name": "delete_enrollments_by_id", "method": "DELETE", "path": "/v1/enrollments/{id}", "title": "Delete an enrollment"},
    {"name": "post_enrollments_by_id_submit", "method": "POST", "path": "/v1/enrollments/{id}/submit", "title": "Submit a draft"},
    {"name": "post_enrollments_by_id_cancel", "method": "POST", "path": "/v1/enrollments/{id}/cancel", "title": "Cancel an enrollment"},
    {"name": "post_tasks_by_id", "method": "POST", "path": "/v1/tasks/{id}", "title": "Answer a task"},
    {"name": "post_enrollments_by_id_documents", "method": "POST", "path": "/v1/enrollments/{id}/documents", "title": "Upload a PDF for a task"},
    {"name": "post_documents_by_id_confirm", "method": "POST", "path": "/v1/documents/{id}/confirm", "title": "Confirm an upload"},
    {"name": "get_documents_by_id", "method": "GET", "path": "/v1/documents/{id}", "title": "Download a document"},
    {"name": "delete_documents_by_id", "method": "DELETE", "path": "/v1/documents/{id}", "title": "Delete a document"},
    {"name": "get_enrollment_requirements", "method": "GET", "path": "/v1/enrollment-requirements", "title": "Payers with published steps"},
    {"name": "get_enrollment_requirements_by_payer_id", "method": "GET", "path": "/v1/enrollment-requirements/{payer_id}", "title": "What a payer asks"},
    {"name": "post_sandbox_enrollments_by_id_advance", "method": "POST", "path": "/v1/sandbox/enrollments/{id}/advance", "title": "Advance a sandbox enrollment"},
    {"name": "post_enrollments_import_check", "method": "POST", "path": "/v1/enrollments/import/check", "title": "Check an enrollment import"},
    {"name": "post_enrollments_import", "method": "POST", "path": "/v1/enrollments/import", "title": "Import enrollment requests"},
    {"name": "get_enrollments_export", "method": "GET", "path": "/v1/enrollments/export", "title": "Export your enrollments"},
    {"name": "get_enrollments_durations", "method": "GET", "path": "/v1/enrollments/durations", "title": "How long your requests took"},
    {"name": "get_payers", "method": "GET", "path": "/v1/payers", "title": "Search the payer directory"},
    {"name": "get_payers_by_id", "method": "GET", "path": "/v1/payers/{id}", "title": "One directory row"},
    {"name": "post_payers_from_card", "method": "POST", "path": "/v1/payers/from-card", "title": "Look up a payer from a card photo"},
    {"name": "get_codes_carc_by_code", "method": "GET", "path": "/v1/codes/carc/{code}", "title": "An adjustment reason code"},
    {"name": "get_codes_group_by_code", "method": "GET", "path": "/v1/codes/group/{code}", "title": "An adjustment group code"},
    {"name": "get_codes_claim_status_by_category", "method": "GET", "path": "/v1/codes/claim-status/{category}", "title": "A claim status category"},
    {"name": "get_codes_claim_status_code_by_code", "method": "GET", "path": "/v1/codes/claim-status-code/{code}", "title": "A claim status code"},
    {"name": "get_codes_service_type_by_code", "method": "GET", "path": "/v1/codes/service-type/{code}", "title": "A service type code"},
    {"name": "get_codes_aaa_by_code", "method": "GET", "path": "/v1/codes/aaa/{code}", "title": "A reject reason code"},
    {"name": "post_sandbox_claims", "method": "POST", "path": "/v1/sandbox/claims", "title": "Run the sandbox payer"},
    {"name": "post_sandbox_claims_837i", "method": "POST", "path": "/v1/sandbox/claims/837i", "title": "Run the sandbox payer on an 837I"},
    {"name": "post_sandbox_claims_837d", "method": "POST", "path": "/v1/sandbox/claims/837d", "title": "Run the sandbox payer on an 837D"},
    {"name": "post_sandbox_eligibility", "method": "POST", "path": "/v1/sandbox/eligibility", "title": "Run the eligibility sandbox"},
    {"name": "post_sandbox_claim_status", "method": "POST", "path": "/v1/sandbox/claim-status", "title": "Run the claim status sandbox"},
    {"name": "post_sandbox_claims_timeline", "method": "POST", "path": "/v1/sandbox/claims/timeline", "title": "Run the sandbox over days"},
    {"name": "get_account", "method": "GET", "path": "/v1/account", "title": "Your account"},
    {"name": "put_account_webhook", "method": "PUT", "path": "/v1/account/webhook", "title": "Set your webhook"},
    {"name": "delete_account_webhook", "method": "DELETE", "path": "/v1/account/webhook", "title": "Remove your webhook"},
    {"name": "post_account_webhook_test", "method": "POST", "path": "/v1/account/webhook/test", "title": "Send a test event"},
    {"name": "get_account_contact", "method": "GET", "path": "/v1/account/contact", "title": "Your notice address"},
    {"name": "put_account_contact", "method": "PUT", "path": "/v1/account/contact", "title": "Set your notice address"},
    {"name": "get_keys", "method": "GET", "path": "/v1/keys", "title": "Your account’s keys"},
    {"name": "post_keys", "method": "POST", "path": "/v1/keys", "title": "Make a key"},
    {"name": "delete_keys_by_id", "method": "DELETE", "path": "/v1/keys/{id}", "title": "Revoke a key"},
    {"name": "post_keys_by_id_rotate", "method": "POST", "path": "/v1/keys/{id}/rotate", "title": "Rotate a key"},
    {"name": "post_mcp", "method": "POST", "path": "/v1/mcp", "title": "The MCP server, hosted"},
    {"name": "get_events", "method": "GET", "path": "/v1/events", "title": "List your events"},
    {"name": "get_events_by_id", "method": "GET", "path": "/v1/events/{id}", "title": "One event"},
    {"name": "post_events_by_id_redeliver", "method": "POST", "path": "/v1/events/{id}/redeliver", "title": "Send an event again"},
    {"name": "get_events_by_id_attempts", "method": "GET", "path": "/v1/events/{id}/attempts", "title": "Every attempt for an event"},
    {"name": "get_webhook_destinations", "method": "GET", "path": "/v1/webhook-destinations", "title": "List your webhook destinations"},
    {"name": "post_webhook_destinations", "method": "POST", "path": "/v1/webhook-destinations", "title": "Add a webhook destination"},
    {"name": "get_webhook_destinations_by_id", "method": "GET", "path": "/v1/webhook-destinations/{id}", "title": "One webhook destination"},
    {"name": "put_webhook_destinations_by_id", "method": "PUT", "path": "/v1/webhook-destinations/{id}", "title": "Change a webhook destination"},
    {"name": "delete_webhook_destinations_by_id", "method": "DELETE", "path": "/v1/webhook-destinations/{id}", "title": "Remove a webhook destination"},
    {"name": "post_webhook_destinations_by_id_ping", "method": "POST", "path": "/v1/webhook-destinations/{id}/ping", "title": "Ping a webhook destination"},
    {"name": "get_webhook_destinations_by_id_deliveries", "method": "GET", "path": "/v1/webhook-destinations/{id}/deliveries", "title": "What a destination received"},
    {"name": "get_sftp", "method": "GET", "path": "/v1/sftp", "title": "Your SFTP user"},
    {"name": "put_sftp", "method": "PUT", "path": "/v1/sftp", "title": "Make your SFTP user"},
    {"name": "delete_sftp", "method": "DELETE", "path": "/v1/sftp", "title": "Remove your SFTP user"},
    {"name": "post_sftp_ssh_keys", "method": "POST", "path": "/v1/sftp/ssh-keys", "title": "Add an SSH key"},
    {"name": "delete_sftp_ssh_keys_by_id", "method": "DELETE", "path": "/v1/sftp/ssh-keys/{id}", "title": "Remove an SSH key"},
    {"name": "post_jobs", "method": "POST", "path": "/v1/jobs", "title": "Make a batch job"},
    {"name": "get_jobs", "method": "GET", "path": "/v1/jobs", "title": "Your batch jobs"},
    {"name": "get_jobs_by_id", "method": "GET", "path": "/v1/jobs/{id}", "title": "One batch job"},
    {"name": "post_jobs_by_id_cancel", "method": "POST", "path": "/v1/jobs/{id}/cancel", "title": "Cancel a batch job"},
    {"name": "delete_jobs_by_id", "method": "DELETE", "path": "/v1/jobs/{id}", "title": "Delete a batch job"},
    {"name": "post_claims_submit", "method": "POST", "path": "/v1/claims/submit", "title": "Send claims to a payer"},
    {"name": "get_partners", "method": "GET", "path": "/v1/partners", "title": "Trading partners"},
    {"name": "get_audit_log", "method": "GET", "path": "/v1/audit-log", "title": "Your account’s audit log"},
    {"name": "get_audit_log_export", "method": "GET", "path": "/v1/audit-log/export", "title": "Download the audit log"},
    {"name": "get_org_roles", "method": "GET", "path": "/v1/org/roles", "title": "The roles"},
    {"name": "get_org_memberships", "method": "GET", "path": "/v1/org/memberships", "title": "Your organizations"},
    {"name": "post_org", "method": "POST", "path": "/v1/org", "title": "Make an organization"},
    {"name": "get_org", "method": "GET", "path": "/v1/org", "title": "Your organization"},
    {"name": "put_org", "method": "PUT", "path": "/v1/org", "title": "Change your organization"},
    {"name": "get_org_members", "method": "GET", "path": "/v1/org/members", "title": "The members"},
    {"name": "put_org_members_by_id", "method": "PUT", "path": "/v1/org/members/{id}", "title": "Change a member’s role"},
    {"name": "delete_org_members_by_id", "method": "DELETE", "path": "/v1/org/members/{id}", "title": "Remove a member"},
    {"name": "get_org_invitations", "method": "GET", "path": "/v1/org/invitations", "title": "The invitations waiting"},
    {"name": "post_org_invitations", "method": "POST", "path": "/v1/org/invitations", "title": "Invite a person"},
    {"name": "delete_org_invitations_by_id", "method": "DELETE", "path": "/v1/org/invitations/{id}", "title": "Cancel an invitation"},
    {"name": "post_org_join", "method": "POST", "path": "/v1/org/join", "title": "Accept an invitation"},
    {"name": "get_org_keys", "method": "GET", "path": "/v1/org/keys", "title": "The account’s keys"},
)

WEBHOOK_TOLERANCE_SECONDS = 300
_RETRY_STATUSES = (429, 503)
_RETRY_CONFLICT = "idempotency_in_flight"
_MAX_RETRIES = 2
_REQUEST_LOG_KEEP = 1000
_RawBody = Union[str, bytes, bytearray, memoryview]


class PayerworksError(Exception):
    """A refusal from the API, or a call that never got an answer.

    status is the HTTP status (0 when no answer came), code the API error code (stable,
    snake_case), message its sentence, and request_id the x-request-id the API answered with:
    quote it when you write to Payerworks. retry_after is what retry-after asked for, in seconds.
    """

    def __init__(self, status: int, code: str, message: str, request_id: Optional[str] = None,
                 body: Any = None, retry_after: Optional[float] = None) -> None:
        super().__init__(message)
        self.status = status
        self.code = code
        self.message = message
        self.request_id = request_id
        self.body = body
        self.retry_after = retry_after

    def __repr__(self) -> str:
        return "PayerworksError(status=%r, code=%r, request_id=%r)" % (self.status, self.code, self.request_id)


def _retry_after_seconds(value: Optional[str]) -> Optional[float]:
    if value is None:
        return None
    text = str(value).strip()
    if re.fullmatch(r"[0-9]+", text):
        return float(text)
    try:
        when = email.utils.parsedate_to_datetime(text)
    except (TypeError, ValueError, IndexError):
        return None
    if when is None:
        return None
    return max(0.0, when.timestamp() - time.time())


def _segment(value: Any, name: str) -> str:
    text = "" if value is None else str(value)
    if not text:
        raise ValueError(name + " is required.")
    if text in (".", ".."):
        raise ValueError(name + " cannot be a dot or two dots: a URL reads them as steps in the path.")
    try:
        return urllib.parse.quote(text, safe="")
    except UnicodeEncodeError:
        raise ValueError(name + " holds text that is not well-formed (a lone surrogate).") from None


def _query_text(value: Any) -> str:
    if value is True:
        return "true"
    if value is False:
        return "false"
    return str(value)


def _query_part(text: str, what: str) -> str:
    """A query name or value, percent-encoded. Text that is not well-formed (a lone surrogate) is the
    caller's mistake: a ValueError naming it, never a codec error from deep inside a call."""
    try:
        return urllib.parse.quote(text, safe="")
    except UnicodeEncodeError:
        raise ValueError(what + " holds text that is not well-formed (a lone surrogate).") from None


# A base URL every URL parser reads the same way: http or https, a host with an optional port, then an
# optional path, in the characters a URL carries as they are. No space, control character or text that
# is not ASCII, no query or fragment (the route is added after the base), and no user name or password.
_BASE_URL_SHAPE = re.compile(r"https?://[A-Za-z0-9._~:\[\]-]+(?:/[A-Za-z0-9._~:/\[\]!$&()*+,;=%-]*)?", re.IGNORECASE | re.ASCII)


def _usable_base_url(base: str) -> bool:
    """The shape above, a host, and a port that parses and is in range: the same rule as the JavaScript client."""
    if not _BASE_URL_SHAPE.fullmatch(base):
        return False
    try:
        parts = urllib.parse.urlsplit(base)
        return bool(parts.hostname) and (parts.port is None or parts.port >= 0)
    except ValueError:
        return False


def _path_text(path: str) -> str:
    """The path as a URL carries it: a space, a control character or non-ASCII text percent-encoded."""
    try:
        return re.sub(r"[\x00-\x20\x7f-\U0010ffff]+", lambda m: urllib.parse.quote(m.group(0), safe=""), path)
    except UnicodeEncodeError:
        raise ValueError("path holds text that is not well-formed (a lone surrogate).") from None


def _redirect_words(location: Optional[str], url: str) -> str:
    """What a redirect is said as: the origin it named, never its path or its query."""
    where = ""
    try:
        parts = urllib.parse.urlsplit(urllib.parse.urljoin(url, str(location or "").strip())) if location else None
        host = parts.hostname if parts is not None else None
        port = parts.port if parts is not None else None
    except ValueError:
        parts, host, port = None, None, None
    if parts is not None and parts.scheme and host:
        shown = "[" + host + "]" if ":" in host else host
        default = {"http": 80, "https": 443}.get(parts.scheme)
        where = " to " + parts.scheme + "://" + shown + ("" if port is None or port == default else ":" + str(port))
    return ("The address answered with a redirect" + where + ". The Payerworks API never redirects, so it was not "
            "followed and nothing was sent there: check the base URL (base_url or PAYERWORKS_BASE_URL).")


class _NoRedirects(urllib.request.HTTPRedirectHandler):
    """The API never redirects: a 3xx is answered as it came, never followed, so the key and the body
    never reach another address. (urllib's default handler follows one, the bearer header included.)"""

    def redirect_request(self, req, fp, code, msg, headers, newurl):  # type: ignore[no-untyped-def]
        return None


_OPENER = urllib.request.build_opener(_NoRedirects)


class Payerworks:
    """The Payerworks API.

    pw = Payerworks(api_key="pw_test_...", base_url="<the address shown with your key>")
    checked = pw.post_claims_validate({"claim": claim})

    Left out, api_key and base_url are read from PAYERWORKS_API_KEY and PAYERWORKS_BASE_URL.
    Each method answers the parsed JSON body; a refusal raises PayerworksError. A POST or PUT
    carries an Idempotency-Key (made here unless you pass one); a 429, a 503 or a 409
    idempotency_in_flight is sent again at most twice, after the wait its retry-after names,
    with the same key. The key is never printed.

    request_log=True keeps YOUR log of calls in this process (pw.request_log()), a number keeps
    that many, a function is handed each entry; request_log_file appends each entry to a file of
    yours as one JSON line. An entry is the method, path, status, request id, timing and
    Idempotency-Key of one attempt: never a body, a query or a header. Payerworks keeps no such record.
    """

    def __init__(self, api_key: Optional[str] = None, base_url: Optional[str] = None, *,
                 timeout: float = 60.0, max_retries: int = _MAX_RETRIES, max_retry_wait: float = 60.0,
                 user_agent: str = "payerworks-python/1",
                 sleep: Optional[Callable[[float], None]] = None,
                 request_log: Union[bool, int, Callable[[Dict[str, Any]], None]] = False,
                 request_log_file: Optional[str] = None) -> None:
        key = os.environ.get("PAYERWORKS_API_KEY", "") if api_key is None else api_key
        url = os.environ.get("PAYERWORKS_BASE_URL", _DEFAULT_BASE_URL) if base_url is None else base_url
        self._api_key = str(key or "").strip()
        self._base_url = str(url or "").strip()
        self._timeout = float(timeout)
        self._max_retries = max(0, min(_MAX_RETRIES, int(max_retries)))
        self._max_retry_wait = float(max_retry_wait)
        self._user_agent = user_agent
        self._sleep = sleep or time.sleep
        if request_log is True:
            self._log_keep = _REQUEST_LOG_KEEP
        elif isinstance(request_log, int) and not isinstance(request_log, bool) and request_log >= 1:
            self._log_keep = int(request_log)
        else:
            self._log_keep = 0
        self._log_sink = request_log if callable(request_log) else None
        self._log_file = str(request_log_file) if request_log_file else None
        self._log: list = []

    def request_log(self) -> list:
        """The request log kept in this process, oldest first: copies. Empty when it keeps nothing."""
        return [dict(e) for e in self._log]

    def clear_request_log(self) -> None:
        """Empty the request log kept in this process."""
        del self._log[:]

    def _note(self, began: float, verb: str, path: str, status: int, request_id: Optional[str],
              key: Optional[str], attempt: int, code: Optional[str]) -> None:
        """One attempt's entry in YOUR request log: never a body, a query or a header. A log that
        fails (a file that cannot be written, a function that raises) never fails a call."""
        if not (self._log_keep or self._log_sink is not None or self._log_file):
            return
        entry = {"at": time.strftime("%Y-%m-%dT%H:%M:%S", time.gmtime(began)) + ".%03dZ" % int((began % 1) * 1000),
                 "method": verb, "path": path, "status": status, "request_id": request_id or None,
                 "ms": int((time.time() - began) * 1000), "idempotency_key": key or None,
                 "attempt": attempt + 1, "error_code": code}
        if self._log_keep:
            self._log.append(entry)
            if len(self._log) > self._log_keep:
                del self._log[:len(self._log) - self._log_keep]
        if self._log_file:
            try:
                fd = os.open(self._log_file, os.O_WRONLY | os.O_CREAT | os.O_APPEND, 0o600)
                with os.fdopen(fd, "a", encoding="utf-8") as fh:
                    fh.write(json.dumps(entry) + "\n")
            except OSError:
                pass
        if self._log_sink is not None:
            try:
                self._log_sink(dict(entry))
            except Exception:  # the log is the caller's: it never fails a call
                pass

    def __repr__(self) -> str:
        return "Payerworks(base_url=%r)" % self._base_url

    def request(self, method: str, path: str, *, body: Any = None, query: Optional[Mapping[str, Any]] = None,
                idempotency_key: Optional[str] = None, raw: bool = False,
                organization: Optional[str] = None, acting_key: Optional[str] = None) -> Any:
        """Any route: the method, the path (with /v1), a JSON body and query parameters.

        organization and acting_key are for a member's console sign-in only (the client made with its
        ID token as api_key): sent as Payerworks-Organization and Payerworks-Key.
        """
        verb = str(method).upper()
        base = self._base_url.rstrip("/")
        if not _usable_base_url(base):
            raise PayerworksError(0, "missing_base_url", "Set base_url (or PAYERWORKS_BASE_URL) to the address shown with your key: http or https and its host, in ASCII with no spaces, and no query, fragment, user name or password.")
        route = _path_text(str(path))
        url = base + (route if route.startswith("/") else "/" + route)
        if query:
            pairs = [_query_part(str(k), "A query parameter name") + "=" + _query_part(_query_text(v), "The query parameter " + str(k))
                     for k, v in query.items() if v is not None]
            if pairs:
                url += ("&" if "?" in url else "?") + "&".join(pairs)
        headers = {"Accept": "application/json", "User-Agent": self._user_agent}
        if self._api_key:
            headers["Authorization"] = "Bearer " + self._api_key
        if organization:
            headers["Payerworks-Organization"] = str(organization)
        if acting_key:
            headers["Payerworks-Key"] = str(acting_key)
        key = idempotency_key or (str(uuid.uuid4()) if verb in ("POST", "PUT") else None)
        if key:
            headers["Idempotency-Key"] = key
        data = None
        if body is not None and verb not in ("GET", "DELETE"):
            headers["Content-Type"] = "application/json"
            data = json.dumps(body).encode("utf-8")
        attempt = 0
        logged_path = str(path).split("?")[0]
        while True:
            began = time.time()
            req = urllib.request.Request(url, data=data, headers=headers, method=verb)
            try:
                try:
                    with _OPENER.open(req, timeout=self._timeout) as res:
                        status = int(res.status)
                        text = res.read().decode("utf-8", "replace")
                        answered = res.headers
                except urllib.error.HTTPError as e:
                    status = int(e.code)
                    answered = e.headers
                    try:
                        text = e.read().decode("utf-8", "replace") if e.fp is not None else ""
                    finally:
                        e.close()
            except http.client.InvalidURL as e:
                self._note(began, verb, logged_path, 0, None, key, attempt, "missing_base_url")
                raise PayerworksError(0, "missing_base_url", "Set base_url (or PAYERWORKS_BASE_URL) to the address shown with your key: %s" % e) from None
            except (urllib.error.URLError, OSError, http.client.HTTPException) as e:
                reason = getattr(e, "reason", e)
                if isinstance(e, socket.timeout) or isinstance(reason, socket.timeout):
                    self._note(began, verb, logged_path, 0, None, key, attempt, "timeout")
                    raise PayerworksError(0, "timeout", "The API did not answer within %s seconds." % self._timeout) from None
                self._note(began, verb, logged_path, 0, None, key, attempt, "connection_failed")
                raise PayerworksError(0, "connection_failed", "The API could not be reached: %s" % (str(reason) or type(e).__name__)) from None
            request_id = answered.get("x-request-id") if answered is not None else None
            if 300 <= status < 400:
                self._note(began, verb, logged_path, status, request_id, key, attempt, "redirected")
                raise PayerworksError(status, "redirected", _redirect_words(answered.get("location") if answered is not None else None, url), request_id)
            parsed: Any = None
            readable = True
            if text:
                try:
                    parsed = json.loads(text)
                except ValueError:
                    readable = False
            if 200 <= status < 300:
                self._note(began, verb, logged_path, status, request_id, key, attempt, None if readable else "unreadable_answer")
                if not readable:
                    raise PayerworksError(status, "unreadable_answer", "The API answered with something that is not JSON.", request_id, text)
                if raw:
                    return {"status": status, "body": parsed, "request_id": request_id, "idempotency_key": key,
                            "headers": {str(k).lower(): v for k, v in (answered.items() if answered is not None else [])}}
                return parsed
            error = parsed.get("error") if readable and isinstance(parsed, dict) and isinstance(parsed.get("error"), dict) else {}
            wait = _retry_after_seconds(answered.get("retry-after") if answered is not None else None)
            again = status in _RETRY_STATUSES or (status == 409 and error.get("code") == _RETRY_CONFLICT)
            self._note(began, verb, logged_path, status, request_id, key, attempt, str(error.get("code") or "http_%d" % status))
            if again and attempt < self._max_retries:
                delay = float(attempt + 1) if wait is None else wait
                if delay <= self._max_retry_wait:
                    self._sleep(delay)
                    attempt += 1
                    continue
            raise PayerworksError(status, str(error.get("code") or "http_%d" % status),
                                  str(error.get("message") or "The API answered %d." % status),
                                  request_id, parsed if readable else text, wait)


    def get_health(self, *, query: Optional[Mapping[str, Any]] = None, idempotency_key: Optional[str] = None, raw: bool = False, organization: Optional[str] = None, acting_key: Optional[str] = None) -> Any:
        """Health: GET /v1/health

        Whether the API is answering. No key needed.
        """
        return self.request("GET", "/v1/health", query=query, idempotency_key=idempotency_key, raw=raw, organization=organization, acting_key=acting_key)

    def post_claims_validate(self, body: Dict[str, Any], *, query: Optional[Mapping[str, Any]] = None, idempotency_key: Optional[str] = None, raw: bool = False, organization: Optional[str] = None, acting_key: Optional[str] = None) -> Any:
        """Validate claims: POST /v1/claims/validate

        Every reason a claim would be refused at the front door or by a payer’s first edit, each
        with a code, the field’s path and a sentence, from the one edit table GET /v1/edits lists:
        the NPIs’ check digits, the dates, the pointers, a total that must equal its lines, the
        filing indicator, a character X12 reserves, and each diagnosis, HCPCS Level II code and
        modifier against the release in force on its date of service (the ICD-10 diagnosis releases
        for fiscal 2025 to 2027, CMS’s HCPCS file from January 2025) and the place of service
        against CMS’s list. A CPT code is checked for its shape only, and a dental D code or a CPT
        modifier, which CMS’s file does not carry, is listed as not checked. Every claim carries
        what the guides require: the submitter’s contact, the billing provider’s tax id, the
        patient’s address and date of birth, a facility’s address when one is sent, and the filing
        indicator. Formatting is repaired first, on a copy, and each claim lists its repairs (phone
        punctuation, a ZIP+4 hyphen, letter case, a diagnosis code’s dot, spaces) and not_checked,
        every check that could not run and why. For a claim to a later payer: the payers’ order,
        each earlier payer’s paid amount, claim and line balance, the adjudication date and the
        patient’s remaining share each at one level, the adjustment groups, the Medicare secondary
        payer type, and remark codes in force on the day; when Medicare is the later payer, the
        claim’s total equals each earlier payer’s paid amount plus all its adjustments, and a claim
        to Medicare is an original claim (frequency 1). Send the claims as JSON, or an 837P file as
        x12, read back claim by claim. Send your own rules beside them as rules, written as POST
        /v1/edits/rules/check reads them, and each is applied to every claim after its repairs: a
        rule that fires is listed as caller_rule with source caller, and one of severity error makes
        the claim invalid like any other error. Passing means the claim is well formed, not that a
        payer will pay it.
        """
        return self.request("POST", "/v1/claims/validate", body=body, query=query, idempotency_key=idempotency_key, raw=raw, organization=organization, acting_key=acting_key)

    def post_claims_837p(self, body: Dict[str, Any], *, query: Optional[Mapping[str, Any]] = None, idempotency_key: Optional[str] = None, raw: bool = False, organization: Optional[str] = None, acting_key: Optional[str] = None) -> Any:
        """Write an 837P: POST /v1/claims/837p

        Up to one hundred claims written as one 005010X222A1 interchange under your sender and
        receiver ids and your control number, a claim to a secondary or tertiary payer included:
        send each earlier payer’s adjudication (otherSubscriberInformation, and
        lineAdjudicationInformation on each line it paid) and the file carries the coordination of
        benefits loops. Every claim is checked as POST /v1/claims/validate checks it, your own rules
        included when you send them as rules, and written from its repaired copy; each claim’s
        answer lists its repairs, what was not checked, and a not_written warning for any field the
        837P has no element for. Nothing is written if any claim would be refused. A test key writes
        test files only.
        """
        return self.request("POST", "/v1/claims/837p", body=body, query=query, idempotency_key=idempotency_key, raw=raw, organization=organization, acting_key=acting_key)

    def post_claims_837i(self, body: Dict[str, Any], *, query: Optional[Mapping[str, Any]] = None, idempotency_key: Optional[str] = None, raw: bool = False, organization: Optional[str] = None, acting_key: Optional[str] = None) -> Any:
        """Write an 837I: POST /v1/claims/837i

        Up to one hundred institutional claims, the UB-04’s electronic form, written as one
        005010X223A2 interchange from the same JSON as the 837P route, with the institutional
        fields: the facility type and frequency, the statement dates, admission type, source and
        patient status, revenue code lines, principal and admitting diagnoses with present on
        admission, procedures, occurrence, span, value and condition codes, and the attending and
        operating providers. Every claim is checked first, and nothing is written if any would be
        refused. A revenue code or a type of bill is checked for its shape: the code set’s words
        belong to the NUBC. A test key writes test files only.
        """
        return self.request("POST", "/v1/claims/837i", body=body, query=query, idempotency_key=idempotency_key, raw=raw, organization=organization, acting_key=acting_key)

    def post_claims_837i_validate(self, body: Dict[str, Any], *, query: Optional[Mapping[str, Any]] = None, idempotency_key: Optional[str] = None, raw: bool = False, organization: Optional[str] = None, acting_key: Optional[str] = None) -> Any:
        """Validate institutional claims: POST /v1/claims/837i/validate

        The same institutional claims checked against the 837I’s rules without writing a file: each
        problem names its code, its field and a sentence.
        """
        return self.request("POST", "/v1/claims/837i/validate", body=body, query=query, idempotency_key=idempotency_key, raw=raw, organization=organization, acting_key=acting_key)

    def post_claims_837d(self, body: Dict[str, Any], *, query: Optional[Mapping[str, Any]] = None, idempotency_key: Optional[str] = None, raw: bool = False, organization: Optional[str] = None, acting_key: Optional[str] = None) -> Any:
        """Write an 837D: POST /v1/claims/837d

        Up to one hundred dental claims and predeterminations written as one 005010X224A2
        interchange from the same JSON as the 837P route, with the teeth and surfaces, the area of
        the mouth, tooth status, prosthesis replacement, orthodontic months and up to four
        diagnoses. A predetermination carries no date of service. A dental procedure code is checked
        for its shape: the code set’s words belong to the ADA. Nothing is written if any claim would
        be refused, and a test key writes test files only.
        """
        return self.request("POST", "/v1/claims/837d", body=body, query=query, idempotency_key=idempotency_key, raw=raw, organization=organization, acting_key=acting_key)

    def post_claims_837d_validate(self, body: Dict[str, Any], *, query: Optional[Mapping[str, Any]] = None, idempotency_key: Optional[str] = None, raw: bool = False, organization: Optional[str] = None, acting_key: Optional[str] = None) -> Any:
        """Validate dental claims: POST /v1/claims/837d/validate

        The same dental claims checked against the 837D’s rules without writing a file: each problem
        names its code, its field and a sentence.
        """
        return self.request("POST", "/v1/claims/837d/validate", body=body, query=query, idempotency_key=idempotency_key, raw=raw, organization=organization, acting_key=acting_key)

    def post_claims_correction_plan(self, body: Dict[str, Any], *, query: Optional[Mapping[str, Any]] = None, idempotency_key: Optional[str] = None, raw: bool = False, organization: Optional[str] = None, acting_key: Optional[str] = None) -> Any:
        """Plan a corrected or void claim: POST /v1/claims/correction-plan

        Say whether you want to replace or void a claim, and give the payer’s claim number, or say
        the payer never processed it (adjudicated: false); with neither, the plan asks which rather
        than guess. The plan says the frequency code, whether REF*F8 goes, whether to use a new
        patient control number, and when a Medicare contractor fixes a processed claim by reopening
        instead. Send the replacement too, as JSON or an 837P (an 837D for a dental claim), and it
        is checked against the plan and every claim rule. Companion guides vary, and the answer says
        so. It writes and sends nothing.
        """
        return self.request("POST", "/v1/claims/correction-plan", body=body, query=query, idempotency_key=idempotency_key, raw=raw, organization=organization, acting_key=acting_key)

    def get_edits(self, *, query: Optional[Mapping[str, Any]] = None, idempotency_key: Optional[str] = None, raw: bool = False, organization: Optional[str] = None, acting_key: Optional[str] = None) -> Any:
        """Every claim edit: GET /v1/edits

        Every check Payerworks runs on a professional claim before it is written: its code, SNIP
        type, severity, what is wrong, how to fix it and where the rule comes from. Also the
        formatting repairs made first, the coordination of benefits checks a claim to a later payer
        gets, the checks a correction plan’s replacement gets, and the checks Payerworks does not
        run, with why. Filter with snip_type, severity, kind or runs_in, applied to every list.
        Passing every check means the claim is well formed and its codes are valid on their dates,
        not that a payer will pay it.

        query: snip_type (Only the edits of this SNIP type.); severity; kind (deterministic: decided
        by the claim itself; code_set: read against a code set.); runs_in (Which runner makes the
        check: validateClaim and edits run on every claim, validateSecondaryClaim on a claim to a
        later payer, correction-plan on a plan’s replacement. Every filter applies to every list; a
        check row has no snip_type or kind, so either filter leaves it out.).
        """
        return self.request("GET", "/v1/edits", query=query, idempotency_key=idempotency_key, raw=raw, organization=organization, acting_key=acting_key)

    def get_edits_by_code(self, code: str, *, query: Optional[Mapping[str, Any]] = None, idempotency_key: Optional[str] = None, raw: bool = False, organization: Optional[str] = None, acting_key: Optional[str] = None) -> Any:
        """One claim edit: GET /v1/edits/{code}

        One check by its code, in any case. A check Payerworks does not run answers runs: false and
        says why; any other code is 404 edit_not_found.
        """
        return self.request("GET", "/v1/edits/" + _segment(code, "code"), query=query, idempotency_key=idempotency_key, raw=raw, organization=organization, acting_key=acting_key)

    def post_x12_parse(self, body: Dict[str, Any], *, query: Optional[Mapping[str, Any]] = None, idempotency_key: Optional[str] = None, raw: bool = False, organization: Optional[str] = None, acting_key: Optional[str] = None) -> Any:
        """Read an X12 file: POST /v1/x12/parse

        Any X12 file Payerworks reads, to JSON with its envelope, in the file’s own delimiters: an
        835, a 999, a 277CA, a 270 or 271, a 276 or 277, a 278, or an 837P, 837I or 837D. A claim
        file is read back to the same JSON its write route takes, each claim also as its own
        one-claim interchange, so a bulk file splits per claim, and every segment the reader does
        not read is named by its id, loop and position, never its value. A 277CA is told from a
        claim status 277 by its implementation guide, any other kind is named, and an interchange
        that mixes kinds is refused.
        """
        return self.request("POST", "/v1/x12/parse", body=body, query=query, idempotency_key=idempotency_key, raw=raw, organization=organization, acting_key=acting_key)

    def post_claims_track(self, body: Dict[str, Any], *, query: Optional[Mapping[str, Any]] = None, idempotency_key: Optional[str] = None, raw: bool = False, organization: Optional[str] = None, acting_key: Optional[str] = None) -> Any:
        """Track claims: POST /v1/claims/track

        Send the claims (as JSON or the 837P) and the 999s, 277CAs and 835s you hold for them. Each
        claim comes back with where it stands (submitted, received, accepted, rejected, processed,
        denied or unknown), who said so, what was paid, and a dated timeline of every file. It reads
        only what you send: nothing is stored and no payer is asked. A payer’s 277 request for
        additional information may ride with the acknowledgments: a request no 275 answers holds the
        claim as information requested, with its due day; send your solicited 275s as attachments to
        see whether they answer it.
        """
        return self.request("POST", "/v1/claims/track", body=body, query=query, idempotency_key=idempotency_key, raw=raw, organization=organization, acting_key=acting_key)

    def post_acknowledgments_explain(self, body: Dict[str, Any], *, query: Optional[Mapping[str, Any]] = None, idempotency_key: Optional[str] = None, raw: bool = False, organization: Optional[str] = None, acting_key: Optional[str] = None) -> Any:
        """Explain an acknowledgment: POST /v1/acknowledgments/explain

        Every rejection in a 999 or a 277CA said as the fix: the segment, the loop, the element, and
        the part of your claim data to correct, matched to your claim reference. A TA1 is said as
        its accept or reject and its note code, in words.
        """
        return self.request("POST", "/v1/acknowledgments/explain", body=body, query=query, idempotency_key=idempotency_key, raw=raw, organization=organization, acting_key=acting_key)

    def post_claims_cms1500(self, body: Dict[str, Any], *, query: Optional[Mapping[str, Any]] = None, idempotency_key: Optional[str] = None, raw: bool = False, organization: Optional[str] = None, acting_key: Optional[str] = None) -> Any:
        """Draw claims on the CMS-1500: POST /v1/claims/cms1500

        Up to one hundred claims in the 837P’s JSON, checked by the same claim checks as POST
        /v1/claims/837p (your own rules are not applied here) and drawn on the CMS-1500 (02/12): six
        service lines a form, as many forms as a claim needs (at most 200 a call), each form’s Box
        28 the total of its own lines. form: full puts CMS’s own form under the data; data_only
        prints the data alone, to print onto pre-printed red forms (print one on plain paper and
        hold it against a form first). Send signed_on to date Boxes 12 and 31, which are otherwise
        left undated, and payer_address for the carrier block. The answer is your PDF as base64;
        nothing is sent or kept.
        """
        return self.request("POST", "/v1/claims/cms1500", body=body, query=query, idempotency_key=idempotency_key, raw=raw, organization=organization, acting_key=acting_key)

    def post_attachments_275(self, body: Dict[str, Any], *, query: Optional[Mapping[str, Any]] = None, idempotency_key: Optional[str] = None, raw: bool = False, organization: Optional[str] = None, acting_key: Optional[str] = None) -> Any:
        """Write a 275 attachment: POST /v1/attachments/275

        A claim’s attachments as an X12 275 (005010X210): each file (PDF, TIFF, JPEG, PNG or a CDA
        document, up to ten and 1.4 MB in all) base64 in its own BIN, under the control number the
        claim’s PWK segment announced with transmission EL, so the payer can match the two; a
        control number the claim does not announce is refused. A control number is 2 to 50 letters,
        digits, dots, dashes or underscores, the same in the claim’s PWK06 and the 275’s TRN02.
        Written as public payer companion guides show the 275, not from the licensed implementation
        guide, and the answer says so: check your payer’s own. A test key writes test files only.
        Names an attachment’s LOINC document type when you give one. A solicited 275 answers a
        payer’s request: send `request` with the request’s trace, and each attachment may echo its
        own requested code (stc01) and name its service line (line_control_number); POST
        /v1/attachments/requests/explain gives every value from the request.
        """
        return self.request("POST", "/v1/attachments/275", body=body, query=query, idempotency_key=idempotency_key, raw=raw, organization=organization, acting_key=acting_key)

    def post_attachments_requests_explain(self, body: Dict[str, Any], *, query: Optional[Mapping[str, Any]] = None, idempotency_key: Optional[str] = None, raw: bool = False, organization: Optional[str] = None, acting_key: Optional[str] = None) -> Any:
        """Read a request for more information: POST /v1/attachments/requests/explain

        A payer’s 277 request for additional information (005010X213) said claim by claim: what it
        asks for (each LOINC code it names, in LOINC’s own words, or said as a code Payerworks has
        no words for), about which claim and which service line, by when against your own today, and
        the solicited 275 that answers it: the payer’s trace, the claim’s REF*EJ, the submitter id,
        the payer’s response contact, and a body POST /v1/attachments/275 takes. Read as public
        sources show the request, not from the licensed implementation guide, and the answer says
        so. Nothing is stored or sent.
        """
        return self.request("POST", "/v1/attachments/requests/explain", body=body, query=query, idempotency_key=idempotency_key, raw=raw, organization=organization, acting_key=acting_key)

    def post_attachments_requests_check(self, body: Dict[str, Any], *, query: Optional[Mapping[str, Any]] = None, idempotency_key: Optional[str] = None, raw: bool = False, organization: Optional[str] = None, acting_key: Optional[str] = None) -> Any:
        """Check a 275 against its request: POST /v1/attachments/requests/check

        A solicited 275 (or up to ten) held to the 277 request it answers before you send it: BGN01
        11, the payer’s trace on every attachment, the request’s REF*EJ and submitter id, every code
        asked for answered by an attachment echoing its STC, each file’s BIN length, and the due
        day. Errors and warnings per 275 set, and per claim whether every code is answered: a set
        with an error of its own answers no code, what is missing is read across every 275 sent, and
        a line’s request is answered only by an attachment carrying that line’s REF*FJ. Nothing is
        stored or sent.
        """
        return self.request("POST", "/v1/attachments/requests/check", body=body, query=query, idempotency_key=idempotency_key, raw=raw, organization=organization, acting_key=acting_key)

    def post_edits_rules_check(self, body: Dict[str, Any], *, query: Optional[Mapping[str, Any]] = None, idempotency_key: Optional[str] = None, raw: bool = False, organization: Optional[str] = None, acting_key: Optional[str] = None) -> Any:
        """Check your own claim rules: POST /v1/edits/rules/check

        Rules you write for your own payers: a field’s path, an operator from a fixed list of
        twenty, a value, the payers it applies to, your message and whether it is an error or a
        warning. Each rule is checked for the format, and applied to any claims you send with it (up
        to 100). The same rules may ride on POST /v1/claims/validate and POST /v1/claims/837p as
        rules, where a rule of severity error refuses the claim. Patterns are a small fixed format,
        never code, and each call has a step budget, and a field a rule reads may reach at most
        1,000 values: a rule it does not reach, or whose field reaches more, is listed as not
        checked with its reason, never as passed. Nothing is stored.
        """
        return self.request("POST", "/v1/edits/rules/check", body=body, query=query, idempotency_key=idempotency_key, raw=raw, organization=organization, acting_key=acting_key)

    def post_eligibility_270(self, body: Dict[str, Any], *, query: Optional[Mapping[str, Any]] = None, idempotency_key: Optional[str] = None, raw: bool = False, organization: Optional[str] = None, acting_key: Optional[str] = None) -> Any:
        """Write a 270: POST /v1/eligibility/270

        Up to one hundred eligibility questions written as one 005010X279A1 interchange, from the
        JSON shape most clearinghouse APIs take: the payer, the provider, the subscriber and at most
        one dependent, the day and the service types. Ask about any day, a later one included; with
        no day named, the payer answers for its own current day. A question may also ask about
        procedures (CPT, HCPCS, CDT or NDC codes with up to four modifiers), point each to the
        patient’s diagnoses, and carry the place of service, a prior authorization or referral
        number, the provider’s taxonomy, a payer PIN and a birth order for twins; a question that
        asks only about procedures asks no service type beside them. Each question is checked field
        by field first, and nothing is written if any would be refused. You are told when a member
        ID begins with the card issuer number (80840), a name holds a title, a degree or a suffix, a
        backtick was written as an apostrophe, or the day is more than a year back. A question that
        uses none of these fields is written exactly as before. A test key writes test files only.
        """
        return self.request("POST", "/v1/eligibility/270", body=body, query=query, idempotency_key=idempotency_key, raw=raw, organization=organization, acting_key=acting_key)

    def post_eligibility_explain(self, body: Dict[str, Any], *, query: Optional[Mapping[str, Any]] = None, idempotency_key: Optional[str] = None, raw: bool = False, organization: Optional[str] = None, acting_key: Optional[str] = None) -> Any:
        """Explain an eligibility answer: POST /v1/eligibility/explain

        Any 271, as X12 or as the JSON most clearinghouse APIs return, said per patient: whether the
        coverage is active on the day you asked about, the plan, and for each service type the
        deductible and out-of-pocket limit with what is met and what remains, in and out of network,
        individual and family, the coinsurance, the copay, and what is not covered or needs prior
        authorization at each tier. A refusal comes back as every reason in words, with the field to
        fix and where to check it, or when to ask again. The JSON may be either shape most
        clearinghouse APIs return, a flat list of benefits or benefits grouped by plan with each
        kind in its own list, and one answer explains the same in any of the three forms; what the
        grouped shape holds that is not read is named. Add ?include=answer for the 271 read to JSON
        as well, and &shape=plans to have it in the grouped shape.

        query: include (answer: add the 271 read to JSON.); shape (With include=answer: plans writes
        the answer grouped by plan, and the answer says so as answer_shape. Without include=answer
        it is refused (invalid_shape).).
        """
        return self.request("POST", "/v1/eligibility/explain", body=body, query=query, idempotency_key=idempotency_key, raw=raw, organization=organization, acting_key=acting_key)

    def post_eligibility_identity_retry(self, body: Dict[str, Any], *, query: Optional[Mapping[str, Any]] = None, idempotency_key: Optional[str] = None, raw: bool = False, organization: Optional[str] = None, acting_key: Optional[str] = None) -> Any:
        """Find the patient again: POST /v1/eligibility/identity-retry

        After a payer could not match the patient (reject reasons 65, 67, 71, 72, 73, 75 or 77, sent
        as reasons or read from the 271 itself): the next questions to try, in order, each built
        from your own question and the facts you give (the legal first name, a previous last name, a
        dependent’s own member ID), each with its changes listed and checked as a 270 is. No name is
        guessed and there is no nickname table; a day and month swapped is offered only as one to
        check first. Suggestions only: Payerworks looks nobody up, and nothing is sent.
        """
        return self.request("POST", "/v1/eligibility/identity-retry", body=body, query=query, idempotency_key=idempotency_key, raw=raw, organization=organization, acting_key=acting_key)

    def post_eligibility_service_type_test(self, body: Dict[str, Any], *, query: Optional[Mapping[str, Any]] = None, idempotency_key: Optional[str] = None, raw: bool = False, organization: Optional[str] = None, acting_key: Optional[str] = None) -> Any:
        """Does a payer answer a service type: POST /v1/eligibility/service-type-test

        Two answers you hold from one payer, one to 30 (or 35, for dental) and one to a specific
        service type: whether the payer answered the specific one, likely ignored it, answered it
        under other service types, or refused it, with how many lines it added and removed. It reads
        only the two answers, as they stood when they were given: a payer may change what it
        answers.
        """
        return self.request("POST", "/v1/eligibility/service-type-test", body=body, query=query, idempotency_key=idempotency_key, raw=raw, organization=organization, acting_key=acting_key)

    def post_eligibility_coordination(self, body: Dict[str, Any], *, query: Optional[Mapping[str, Any]] = None, idempotency_key: Optional[str] = None, raw: bool = False, organization: Optional[str] = None, acting_key: Optional[str] = None) -> Any:
        """Which plan pays first: POST /v1/eligibility/coordination

        The plans you list for one person, two to ten, put in the order they pay by the NAIC
        coordination of benefits model regulation and Medicare’s secondary payer rules, each step
        naming its rule and its section. Medicaid always pays last, and after Medicare a Medigap
        policy comes before TRICARE; injury coverage not related to the service is left out of the
        order. Where a rule needs a fact you did not send, no order is given and the fact is named,
        never guessed. It knows only the plans you send: it cannot find a person’s other coverage.
        """
        return self.request("POST", "/v1/eligibility/coordination", body=body, query=query, idempotency_key=idempotency_key, raw=raw, organization=organization, acting_key=acting_key)

    def post_eligibility_discovery_verify(self, body: Dict[str, Any], *, query: Optional[Mapping[str, Any]] = None, idempotency_key: Optional[str] = None, raw: bool = False, organization: Optional[str] = None, acting_key: Optional[str] = None) -> Any:
        """Is this coverage the patient’s: POST /v1/eligibility/discovery/verify

        Up to ten coverages an insurance discovery search found, each a 271, checked against the
        patient: the last name (a two-part surname allowed), the first name, the exact date of
        birth, the gender and the address. Each is said as a match, one to review or not this
        patient, and one the payer reports as pending is marked to confirm with the payer.
        Payerworks runs no discovery search itself.
        """
        return self.request("POST", "/v1/eligibility/discovery/verify", body=body, query=query, idempotency_key=idempotency_key, raw=raw, organization=organization, acting_key=acting_key)

    def post_eligibility_pdf(self, body: Dict[str, Any], *, query: Optional[Mapping[str, Any]] = None, idempotency_key: Optional[str] = None, raw: bool = False, organization: Optional[str] = None, acting_key: Optional[str] = None) -> Any:
        """Print an eligibility answer: POST /v1/eligibility/pdf

        A 271, as POST /v1/eligibility/explain takes it, as a summary per patient: what was asked
        and what the answer echoes back, whether the coverage is active, the plan’s figures in and
        out of network, each service type asked, and every benefit line by plan and then service
        type. Up to 50 patients and 3,000 benefit lines a call, and 3,000 rows as drawn: a benefit
        line is listed under each service type it names, so a line naming ten service types is ten
        rows (and each of its messages one more under each). The answer is your PDF as base64.
        """
        return self.request("POST", "/v1/eligibility/pdf", body=body, query=query, idempotency_key=idempotency_key, raw=raw, organization=organization, acting_key=acting_key)

    def post_claim_status_276(self, body: Dict[str, Any], *, query: Optional[Mapping[str, Any]] = None, idempotency_key: Optional[str] = None, raw: bool = False, organization: Optional[str] = None, acting_key: Optional[str] = None) -> Any:
        """Write a 276: POST /v1/claim-status/276

        Up to one hundred claim status questions, from the JSON shape most clearinghouse APIs take,
        written as one 005010X212 interchange for you to send on your own connection. Each question
        is checked field by field first, and nothing is written if any would be refused. A test key
        writes test files only.
        """
        return self.request("POST", "/v1/claim-status/276", body=body, query=query, idempotency_key=idempotency_key, raw=raw, organization=organization, acting_key=acting_key)

    def post_claim_status_explain(self, body: Dict[str, Any], *, query: Optional[Mapping[str, Any]] = None, idempotency_key: Optional[str] = None, raw: bool = False, organization: Optional[str] = None, acting_key: Optional[str] = None) -> Any:
        """Explain a claim status answer: POST /v1/claim-status/explain

        A 277 claim status answer, as X12 or as the JSON most clearinghouse APIs return, said per
        question: where the claim stands, whether money is coming and how much, what to do next and
        when to ask again. A claim the payer cannot find yet is never called a rejection, and a
        question the payer refused because of its own data is never asked again unchanged. A claim
        the payer’s search did not find comes back with the next questions to ask, in order, each
        built from the question you sent (asked.request) and what you hold (asked.known), never from
        a value you did not give; a payer whose system did not answer, with a retry schedule for the
        same day.
        """
        return self.request("POST", "/v1/claim-status/explain", body=body, query=query, idempotency_key=idempotency_key, raw=raw, organization=organization, acting_key=acting_key)

    def post_claims_follow_up(self, body: Dict[str, Any], *, query: Optional[Mapping[str, Any]] = None, idempotency_key: Optional[str] = None, raw: bool = False, organization: Optional[str] = None, acting_key: Optional[str] = None) -> Any:
        """Follow up on open claims: POST /v1/claims/follow-up

        Your open claims (up to 200) with the day each was sent, and your history of when each payer
        paid (up to 20,000 rows), or the 837Ps, 277CAs and 835s themselves. Each payer’s days to pay
        are learned from your history alone, with how many paid claims they rest on, and only from
        five up; below that the answer says there is not enough history. Each claim comes back with
        when its remittance is expected, whether it is late, and the next step: ask its status now,
        wait until a day, the next questions after a not found, or retries for a payer whose system
        did not answer. Your own follow-up codes come back as you sent them, never read. Nothing is
        stored, nothing is sent, and no payer is asked.
        """
        return self.request("POST", "/v1/claims/follow-up", body=body, query=query, idempotency_key=idempotency_key, raw=raw, organization=organization, acting_key=acting_key)

    def post_remittances_explain(self, body: Dict[str, Any], *, query: Optional[Mapping[str, Any]] = None, idempotency_key: Optional[str] = None, raw: bool = False, organization: Optional[str] = None, acting_key: Optional[str] = None) -> Any:
        """Explain a remittance: POST /v1/remittances/explain

        An 835 claim by claim: what was paid, allowed and written off, the patient’s share, the
        verdict in words, each service line, every adjustment and remark code in words, and whether
        each check adds up to the claims it pays.
        """
        return self.request("POST", "/v1/remittances/explain", body=body, query=query, idempotency_key=idempotency_key, raw=raw, organization=organization, acting_key=acting_key)

    def post_denials_next_step(self, body: Dict[str, Any], *, query: Optional[Mapping[str, Any]] = None, idempotency_key: Optional[str] = None, raw: bool = False, organization: Optional[str] = None, acting_key: Optional[str] = None) -> Any:
        """The next step on a denial: POST /v1/denials/next-step

        An 835, or the claims as JSON: for each claim and each adjustment, what the payer did,
        whether the patient may be billed, the next step, the corrected claim fields to send, what
        to attach, and every deadline with where it comes from: a Medicare regulation with its
        section, the limits you send, or no date at all.
        """
        return self.request("POST", "/v1/denials/next-step", body=body, query=query, idempotency_key=idempotency_key, raw=raw, organization=organization, acting_key=acting_key)

    def post_appeals_letter(self, body: Dict[str, Any], *, query: Optional[Mapping[str, Any]] = None, idempotency_key: Optional[str] = None, raw: bool = False, organization: Optional[str] = None, acting_key: Optional[str] = None) -> Any:
        """Write an appeal letter: POST /v1/appeals/letter

        An appeal letter written from the facts you send, by a fixed template for its kind. It never
        adds a code, an amount or a date you did not give. A fact the kind needs and you left out is
        named, and nothing is written.
        """
        return self.request("POST", "/v1/appeals/letter", body=body, query=query, idempotency_key=idempotency_key, raw=raw, organization=organization, acting_key=acting_key)

    def post_appeals_letter_check(self, body: Dict[str, Any], *, query: Optional[Mapping[str, Any]] = None, idempotency_key: Optional[str] = None, raw: bool = False, organization: Optional[str] = None, acting_key: Optional[str] = None) -> Any:
        """Check an edited letter: POST /v1/appeals/letter/check

        A letter you edited, held to the facts by the same rule: every code, amount and date it
        states, however it is written (a dollar sign or the word dollars, a written or numeric date,
        CO-50 or CARC 50, N115 or N 115), must be one the facts hold, and a placeholder left in is
        found.
        """
        return self.request("POST", "/v1/appeals/letter/check", body=body, query=query, idempotency_key=idempotency_key, raw=raw, organization=organization, acting_key=acting_key)

    def post_remittances_pdf(self, body: Dict[str, Any], *, query: Optional[Mapping[str, Any]] = None, idempotency_key: Optional[str] = None, raw: bool = False, organization: Optional[str] = None, acting_key: Optional[str] = None) -> Any:
        """Print a remittance: POST /v1/remittances/pdf

        An 835 laid out the way Medicare’s Standard Paper Remittance is read: each check, each claim
        and its service lines with what was billed, allowed, applied to the deductible and
        coinsurance, adjusted and paid, the check’s totals and provider-level adjustments, and a
        glossary of every code on the file. Up to 500 claims and 3,000 service lines a call. The
        answer is your PDF as base64.
        """
        return self.request("POST", "/v1/remittances/pdf", body=body, query=query, idempotency_key=idempotency_key, raw=raw, organization=organization, acting_key=acting_key)

    def post_appeals_form(self, body: Dict[str, Any], *, query: Optional[Mapping[str, Any]] = None, idempotency_key: Optional[str] = None, raw: bool = False, organization: Optional[str] = None, acting_key: Optional[str] = None) -> Any:
        """Fill a Medicare appeal form: POST /v1/appeals/form

        CMS’s own redetermination request (CMS-20027) or reconsideration request (CMS-20033), the
        file CMS publishes, filled from the claim and the 835 that denied it: the beneficiary, the
        Medicare number, the codes of the lines the payer denied (a line it paid is never listed)
        and their dates of service, the notice date, and why you disagree in your words. Original
        Medicare only. Nothing is signed, dated or chosen for you, the deadline comes with its
        regulation, and the form is yours to review and send.
        """
        return self.request("POST", "/v1/appeals/form", body=body, query=query, idempotency_key=idempotency_key, raw=raw, organization=organization, acting_key=acting_key)

    def post_remittances_from_image(self, body: Dict[str, Any], *, query: Optional[Mapping[str, Any]] = None, idempotency_key: Optional[str] = None, raw: bool = False, organization: Optional[str] = None, acting_key: Optional[str] = None) -> Any:
        """Read a paper EOB into a draft 835: POST /v1/remittances/from-image

        A photo, scan or PDF of a payer’s paper explanation of benefits (two pages at most, 1.4 MB),
        read by a model into a DRAFT: every figure, date and code with the reader’s confidence and
        where on the page it was read, the payments checked by arithmetic against the check total
        the page states, and an 835 written as a test file. It is never an ERA and nothing is
        posted: check every field against the paper. Off until Payerworks turns it on; test keys and
        invented documents only for now, at most 100 reads a UTC day for an account’s keys together;
        a call refused for the route being off, a live key or no file is not counted. A read has 25
        seconds; past them the answer is 504 model_read_timed_out: send one page, or a crop of the
        part you need. Nothing is kept.
        """
        return self.request("POST", "/v1/remittances/from-image", body=body, query=query, idempotency_key=idempotency_key, raw=raw, organization=organization, acting_key=acting_key)

    def post_remittances_duplicates(self, body: Dict[str, Any], *, query: Optional[Mapping[str, Any]] = None, idempotency_key: Optional[str] = None, raw: bool = False, organization: Optional[str] = None, acting_key: Optional[str] = None) -> Any:
        """Find duplicate remittances: POST /v1/remittances/duplicates

        Send several 835s and get back every check that is one payment sent more than once: the same
        trace number (TRN02) from the same originator (TRN03), never the trace alone. Each duplicate
        says whether the copies are the same remittance or differ in content, and a check with no
        trace is listed apart. Nothing is stored.
        """
        return self.request("POST", "/v1/remittances/duplicates", body=body, query=query, idempotency_key=idempotency_key, raw=raw, organization=organization, acting_key=acting_key)

    def post_remittances_reassociate(self, body: Dict[str, Any], *, query: Optional[Mapping[str, Any]] = None, idempotency_key: Optional[str] = None, raw: bool = False, organization: Optional[str] = None, acting_key: Optional[str] = None) -> Any:
        """Match deposits to remittances: POST /v1/remittances/reassociate

        Send 835s and the deposits from your bank (the amount and effective date, and the trace
        number or description when your bank gives them). Each deposit is matched to one check by
        its trace number first, then by a trace in the description, then by amount and date only
        when exactly one check and one deposit fit, and every match says what agreed and what did
        not. Unmatched deposits and checks are listed. No bank description is repeated back.
        """
        return self.request("POST", "/v1/remittances/reassociate", body=body, query=query, idempotency_key=idempotency_key, raw=raw, organization=organization, acting_key=acting_key)

    def post_remittances_split(self, body: Dict[str, Any], *, query: Optional[Mapping[str, Any]] = None, idempotency_key: Optional[str] = None, raw: bool = False, organization: Optional[str] = None, acting_key: Optional[str] = None) -> Any:
        """Split an 835 by payee or rendering provider: POST /v1/remittances/split

        Send an 835 paid to one tax ID and get one file per payee (the provider the payer grouped
        the claims under) or per rendering NPI. Each file’s payment total is recomputed from its own
        claims and provider-level adjustments and read back to check it balances, the parts add up
        to the original check, and each keeps its trace. An adjustment that names no single group
        gets a file of its own, never a guessed one, and a value holding a delimiter is refused,
        never rewritten. Send the interchange control number you track for the first file (each
        further file takes the next): the original’s own number is never reused. The files are yours
        to load; nothing is sent.
        """
        return self.request("POST", "/v1/remittances/split", body=body, query=query, idempotency_key=idempotency_key, raw=raw, organization=organization, acting_key=acting_key)

    def post_remittances_recoupments(self, body: Dict[str, Any], *, query: Optional[Mapping[str, Any]] = None, idempotency_key: Optional[str] = None, raw: bool = False, organization: Optional[str] = None, acting_key: Optional[str] = None) -> Any:
        """Match recoupments to the claims they take back: POST /v1/remittances/recoupments

        Send a set of 835s. Every provider-level adjustment is named, and each takeback is matched
        to the paid claim its reference names in the files you sent: by payer claim number, then
        patient control number, then amount when only one earlier payment at the same payer fits.
        Unmatched takebacks are listed, with what each payer took back and what the files show still
        carried forward. It says what was taken and from which claim, never whether it should have
        been.
        """
        return self.request("POST", "/v1/remittances/recoupments", body=body, query=query, idempotency_key=idempotency_key, raw=raw, organization=organization, acting_key=acting_key)

    def get_filing_limits(self, *, query: Optional[Mapping[str, Any]] = None, idempotency_key: Optional[str] = None, raw: bool = False, organization: Optional[str] = None, acting_key: Optional[str] = None) -> Any:
        """Published claim filing windows: GET /v1/filing-limits

        The claim filing window each payer in Payerworks’ table publishes, read on its own pages on
        09/10/2026, and Medicare fee-for-service’s from 42 CFR 424.44. Search with q, and add
        date_of_service to count the last day where the payer states one figure; a payer whose pages
        leave the window to its agreement says so, with no day. Your agreement with the payer
        governs over a published window, and nothing here is a payer’s answer about a claim.

        query: q (Part of a payer’s name.); date_of_service (YYYY-MM-DD: count the last day where
        the payer states one figure.).
        """
        return self.request("GET", "/v1/filing-limits", query=query, idempotency_key=idempotency_key, raw=raw, organization=organization, acting_key=acting_key)

    def get_filing_limits_by_payer(self, payer: str, *, query: Optional[Mapping[str, Any]] = None, idempotency_key: Optional[str] = None, raw: bool = False, organization: Optional[str] = None, acting_key: Optional[str] = None) -> Any:
        """One payer’s filing window: GET /v1/filing-limits/{payer}

        One payer by its slug from GET /v1/filing-limits, or by its name, with every plan line it
        publishes a window for; add date_of_service to count the last day. A payer with no window on
        record is 404 no_filing_limit_on_record, never a window.

        query: date_of_service (YYYY-MM-DD: count the last day where the payer states one figure.).
        """
        return self.request("GET", "/v1/filing-limits/" + _segment(payer, "payer"), query=query, idempotency_key=idempotency_key, raw=raw, organization=organization, acting_key=acting_key)

    def post_analytics_summary(self, body: Dict[str, Any], *, query: Optional[Mapping[str, Any]] = None, idempotency_key: Optional[str] = None, raw: bool = False, organization: Optional[str] = None, acting_key: Optional[str] = None) -> Any:
        """Summarize your claims: POST /v1/analytics/summary

        Send the claims with the 999s, 277CAs and 835s you hold for them (the same body as
        /v1/claims/track), or your 835s alone. You get first-pass acceptance, the rejection reasons
        ranked, the denial rate by payer and by group code, the dollars refused, the days from
        sending a claim to its remittance by payer (the median and quartiles), reversals, and what
        the refusals come down to by reason. Every figure says how many it counts, and a rate or a
        median is read as a pattern only from five; below that the answer says there is not enough
        history. Send month (YYYY-MM) for one calendar month: the current month counts to today. It
        reads only your files: there is no benchmark, and nothing is stored.
        """
        return self.request("POST", "/v1/analytics/summary", body=body, query=query, idempotency_key=idempotency_key, raw=raw, organization=organization, acting_key=acting_key)

    def post_remittances_variance(self, body: Dict[str, Any], *, query: Optional[Mapping[str, Any]] = None, idempotency_key: Optional[str] = None, raw: bool = False, organization: Optional[str] = None, acting_key: Optional[str] = None) -> Any:
        """Check payments against your fee schedule: POST /v1/remittances/variance

        Send your 835s and your fee schedule (payer_id, code, modifier, allowed, effective_from,
        effective_to). Each service line is judged against the rate in force on its date of service:
        the amount expected, the payer’s allowed amount, the difference, and the adjustment codes
        the payer gave. A line with no rate says so and is never read as zero, and a line the payer
        refused is a denial, not an underpayment. Add medicare (a state) to compare with Medicare’s
        October 2026 DMEPOS fee schedule where the API runs, labeled as Medicare’s rate and never as
        your contract. An underpaid line carries the facts for POST /v1/appeals/letter.
        """
        return self.request("POST", "/v1/remittances/variance", body=body, query=query, idempotency_key=idempotency_key, raw=raw, organization=organization, acting_key=acting_key)

    def post_claims_risk(self, body: Dict[str, Any], *, query: Optional[Mapping[str, Any]] = None, idempotency_key: Optional[str] = None, raw: bool = False, organization: Optional[str] = None, acting_key: Optional[str] = None) -> Any:
        """Check a claim against your past denials: POST /v1/claims/risk

        Send a claim (as /v1/claims/validate takes it) and your past 835s. For each line, every past
        refusal of the same payer, procedure code and modifiers, with its reason and remark codes,
        on how many claims, out of how many decided claims with a line of that shape, and the fix. A
        reason is called a pattern only from two refused claims: a claim counts once however many of
        its lines were refused, and a remittance sent twice counts once. Send the past claims too
        and the diagnosis is compared. It reads only your files and never says how a payer will
        decide.
        """
        return self.request("POST", "/v1/claims/risk", body=body, query=query, idempotency_key=idempotency_key, raw=raw, organization=organization, acting_key=acting_key)

    def post_denials_prioritize(self, body: Dict[str, Any], *, query: Optional[Mapping[str, Any]] = None, idempotency_key: Optional[str] = None, raw: bool = False, organization: Optional[str] = None, acting_key: Optional[str] = None) -> Any:
        """Rank your open denials: POST /v1/denials/prioritize

        Send the 835s holding your open denials, and if you have them, your appeal outcomes
        (payer_id, carc, outcome). Denials due in 14 days or fewer come first, soonest first; then
        each by its dollars, or its dollars times your own overturn rate for that payer and code
        once you have five decided appeals there. A passed deadline comes last. Without outcomes it
        ranks by deadline and dollars, and says so. No rate here is anyone’s but yours.
        """
        return self.request("POST", "/v1/denials/prioritize", body=body, query=query, idempotency_key=idempotency_key, raw=raw, organization=organization, acting_key=acting_key)

    def post_authorizations_278(self, body: Dict[str, Any], *, query: Optional[Mapping[str, Any]] = None, idempotency_key: Optional[str] = None, raw: bool = False, organization: Optional[str] = None, acting_key: Optional[str] = None) -> Any:
        """Write a 278: POST /v1/authorizations/278

        Up to one hundred prior authorization or referral requests, from JSON (the kind of review,
        the service type, each procedure code with its units and dates, the requester, the servicing
        provider, the patient and the diagnoses), written as one 005010X217 interchange for you to
        send on your own connection, or built in the shape of the Da Vinci PAS request bundle with
        output set to fhir. Each request is checked field by field first, and nothing is written if
        any would be refused. A test key writes test files only. Whether a payer takes a 278 at all
        is the payer’s choice: many take prior authorizations only on a portal, by fax or by phone.
        """
        return self.request("POST", "/v1/authorizations/278", body=body, query=query, idempotency_key=idempotency_key, raw=raw, organization=organization, acting_key=acting_key)

    def post_authorizations_explain(self, body: Dict[str, Any], *, query: Optional[Mapping[str, Any]] = None, idempotency_key: Optional[str] = None, raw: bool = False, organization: Optional[str] = None, acting_key: Optional[str] = None) -> Any:
        """Explain a 278 response: POST /v1/authorizations/explain

        A payer’s 278 response, as X12, as Payerworks’ own JSON read of one, or as a PAS response,
        said per review: approved, approved in part, approved with changes, not approved, pended,
        waiting on more information, canceled, no review needed, or not reviewed at all, with the
        reason in words, the certification and reference numbers, the approved dates and units
        against what you asked, the claim field the number goes in, and the next step. A request the
        payer never reviewed is never called a denial.

        query: include (answer: add the 278 read to JSON.).
        """
        return self.request("POST", "/v1/authorizations/explain", body=body, query=query, idempotency_key=idempotency_key, raw=raw, organization=organization, acting_key=acting_key)

    def post_sandbox_authorizations(self, body: Dict[str, Any], *, query: Optional[Mapping[str, Any]] = None, idempotency_key: Optional[str] = None, raw: bool = False, organization: Optional[str] = None, acting_key: Optional[str] = None) -> Any:
        """Run the prior authorization sandbox: POST /v1/sandbox/authorizations

        The round trip on invented data: your requests written as a 278 and the sandbox payer’s 278
        response, chosen by how each member ID ends, read back and explained in the same call. Any
        key may call it, every file is a test file, and nothing reaches a payer.
        """
        return self.request("POST", "/v1/sandbox/authorizations", body=body, query=query, idempotency_key=idempotency_key, raw=raw, organization=organization, acting_key=acting_key)

    def post_authorizations_required(self, body: Dict[str, Any], *, query: Optional[Mapping[str, Any]] = None, idempotency_key: Optional[str] = None, raw: bool = False, organization: Optional[str] = None, acting_key: Optional[str] = None) -> Any:
        """Does Medicare require it: POST /v1/authorizations/required

        Up to one hundred procedure codes checked against CMS’s published Medicare fee-for-service
        lists on a date of service: the DMEPOS Required Prior Authorization List and the hospital
        outpatient department list, each named and dated, with the state or the setting asked for
        where the list depends on it. A code on neither list is said as not on the lists held, never
        as not required, and for any other payer the answer is that no published list is held.
        """
        return self.request("POST", "/v1/authorizations/required", body=body, query=query, idempotency_key=idempotency_key, raw=raw, organization=organization, acting_key=acting_key)

    def post_estimates(self, body: Dict[str, Any], *, query: Optional[Mapping[str, Any]] = None, idempotency_key: Optional[str] = None, raw: bool = False, organization: Optional[str] = None, acting_key: Optional[str] = None) -> Any:
        """Estimate a patient’s share: POST /v1/estimates

        A patient’s share and the plan’s, line by line, from a 271 (X12 or JSON) and the allowed
        amounts you send, at most 50 lines. You say the network level and, where a copay is stated,
        how it applies (copay_rule): a 271 says neither. The lines draw on the deductible and the
        out-of-pocket amount left in the order sent; for one member, a family amount left the 271
        states bounds the member’s, and since a 271 does not say whether a family deductible is
        embedded or aggregate, the answer says which it read and what the other would mean. Where
        the out-of-pocket limit caps a line, its deductible and coinsurance are cut to add up to the
        share. Every input names where it came from: the 271, you, or not stated. A benefit the 271
        did not state stops that line rather than reading as zero, unless you supply it, and no
        allowed amount is ever supplied for you: Payerworks holds no prices. The payer’s own
        processing decides what the patient owes.
        """
        return self.request("POST", "/v1/estimates", body=body, query=query, idempotency_key=idempotency_key, raw=raw, organization=organization, acting_key=acting_key)

    def post_estimates_good_faith(self, body: Dict[str, Any], *, query: Optional[Mapping[str, Any]] = None, idempotency_key: Optional[str] = None, raw: bool = False, organization: Optional[str] = None, acting_key: Optional[str] = None) -> Any:
        """Lay out a Good Faith Estimate: POST /v1/estimates/good-faith

        A Good Faith Estimate for an uninsured or self-pay patient, with every element 45 CFR
        149.610 lists, in order and each named by its paragraph: the patient, the primary service,
        the items grouped by provider with their codes and expected charges, the amount at which the
        patient may dispute the bill, the disclaimers, and when it is due by the business days you
        state. It is your document to issue, give and keep; Payerworks issues, delivers and keeps
        nothing.
        """
        return self.request("POST", "/v1/estimates/good-faith", body=body, query=query, idempotency_key=idempotency_key, raw=raw, organization=organization, acting_key=acting_key)

    def post_dme_replacement_window(self, body: Dict[str, Any], *, query: Optional[Mapping[str, Any]] = None, idempotency_key: Optional[str] = None, raw: bool = False, organization: Optional[str] = None, acting_key: Optional[str] = None) -> Any:
        """When equipment may be replaced: POST /v1/dme/replacement-window

        For each item you send (a HCPCS code and the day it was delivered, at most 100): the
        earliest replacement for wear under Medicare’s reasonable useful lifetime, five years from
        delivery unless you name a longer lifetime and its source, whether a planned day falls
        inside it (judged from the latest delivery you sent of the same code), and what to consider
        by reason (an Advance Beneficiary Notice with GA or GZ, or the RA modifier for a loss, a
        theft or irreparable damage), each cited. It does not check Medicare’s records: only the DME
        MAC shows whether a same or similar item was supplied.
        """
        return self.request("POST", "/v1/dme/replacement-window", body=body, query=query, idempotency_key=idempotency_key, raw=raw, organization=organization, acting_key=acting_key)

    def post_financial_assistance_fpl(self, body: Dict[str, Any], *, query: Optional[Mapping[str, Any]] = None, idempotency_key: Optional[str] = None, raw: bool = False, organization: Optional[str] = None, acting_key: Optional[str] = None) -> Any:
        """Income against the poverty guideline: POST /v1/financial-assistance/fpl

        Income as a percent of the 2026 HHS poverty guideline for the household’s size (Alaska and
        Hawaii on their own tables; a territory only when you say your program uses the contiguous
        table), and which of your policy’s tiers it falls in, compared in whole cents. Arithmetic
        only: no screening and no decision.
        """
        return self.request("POST", "/v1/financial-assistance/fpl", body=body, query=query, idempotency_key=idempotency_key, raw=raw, organization=organization, acting_key=acting_key)

    def post_price_transparency_validate(self, body: Dict[str, Any], *, query: Optional[Mapping[str, Any]] = None, idempotency_key: Optional[str] = None, raw: bool = False, organization: Optional[str] = None, acting_key: Optional[str] = None) -> Any:
        """Check a hospital standard charges file: POST /v1/price-transparency/validate

        A hospital’s standard charges file, as the tall or wide CSV or as JSON, checked against
        CMS’s version 3.0.0 template and schema: every required header and value, the valid values,
        and each conditional requirement, each problem with its row or path, the column and the rule
        it breaks. A file over 2 MB is checked in pieces, the first three rows sent again with each
        and row_offset set, so every row number is the file’s own. Nothing is hosted, published or
        kept.
        """
        return self.request("POST", "/v1/price-transparency/validate", body=body, query=query, idempotency_key=idempotency_key, raw=raw, organization=organization, acting_key=acting_key)

    def post_providers(self, body: Dict[str, Any], *, query: Optional[Mapping[str, Any]] = None, idempotency_key: Optional[str] = None, raw: bool = False, organization: Optional[str] = None, acting_key: Optional[str] = None) -> Any:
        """Create a provider: POST /v1/providers

        A provider record: its name, billing NPI, tax id (an EIN, never a Social Security number)
        and contacts. One per NPI and tax id pair: a second is refused, naming the first. With a
        live key the answer also says what the NPI registry holds for the NPI: whether it is there,
        Type 1 or 2, and whether the provider’s name matches (its own name only, never a contact’s;
        a warning, never a refusal; a lookup that fails says so, and is never read as not found).
        """
        return self.request("POST", "/v1/providers", body=body, query=query, idempotency_key=idempotency_key, raw=raw, organization=organization, acting_key=acting_key)

    def get_providers(self, *, query: Optional[Mapping[str, Any]] = None, idempotency_key: Optional[str] = None, raw: bool = False, organization: Optional[str] = None, acting_key: Optional[str] = None) -> Any:
        """List your providers: GET /v1/providers

        Newest first. Page with limit (up to 100) and starting_after, and filter with npi, tax_id or
        q (part of the name).

        query: limit (How many a page holds, 1 to 100.); starting_after (The next_cursor of the page
        before, or the id of a provider on it.); npi (Only the provider with this NPI.); tax_id
        (Only providers with this EIN, with or without the hyphen.); q (Part of the name, any
        case.).
        """
        return self.request("GET", "/v1/providers", query=query, idempotency_key=idempotency_key, raw=raw, organization=organization, acting_key=acting_key)

    def get_providers_by_id(self, id: str, *, query: Optional[Mapping[str, Any]] = None, idempotency_key: Optional[str] = None, raw: bool = False, organization: Optional[str] = None, acting_key: Optional[str] = None) -> Any:
        """One provider: GET /v1/providers/{id}

        The provider, and whether its NPI and tax id are locked because a request for it has gone to
        a payer.
        """
        return self.request("GET", "/v1/providers/" + _segment(id, "id"), query=query, idempotency_key=idempotency_key, raw=raw, organization=organization, acting_key=acting_key)

    def post_providers_by_id(self, id: str, body: Dict[str, Any], *, query: Optional[Mapping[str, Any]] = None, idempotency_key: Optional[str] = None, raw: bool = False, organization: Optional[str] = None, acting_key: Optional[str] = None) -> Any:
        """Change a provider: POST /v1/providers/{id}

        Only the fields you send change. The NPI and tax id can be corrected until a request for the
        provider has gone to a payer.
        """
        return self.request("POST", "/v1/providers/" + _segment(id, "id"), body=body, query=query, idempotency_key=idempotency_key, raw=raw, organization=organization, acting_key=acting_key)

    def delete_providers_by_id(self, id: str, *, query: Optional[Mapping[str, Any]] = None, idempotency_key: Optional[str] = None, raw: bool = False, organization: Optional[str] = None, acting_key: Optional[str] = None) -> Any:
        """Delete a provider: DELETE /v1/providers/{id}

        Its drafts go with it. A provider with a request that is open or live is kept, and the
        refusal names those requests. Deleting again answers the same.
        """
        return self.request("DELETE", "/v1/providers/" + _segment(id, "id"), query=query, idempotency_key=idempotency_key, raw=raw, organization=organization, acting_key=acting_key)

    def post_enrollments(self, body: Dict[str, Any], *, query: Optional[Mapping[str, Any]] = None, idempotency_key: Optional[str] = None, raw: bool = False, organization: Optional[str] = None, acting_key: Optional[str] = None) -> Any:
        """Request an enrollment: POST /v1/enrollments

        One request per provider, payer and transaction: eligibility, claim_status,
        professional_claims or remittances. Kept as a draft, or handed to Payerworks with submit:
        true. Payerworks is not yet registered with any payer, so a live request waits with
        Payerworks, the answer carries a route_not_open warning, and nothing is sent.
        """
        return self.request("POST", "/v1/enrollments", body=body, query=query, idempotency_key=idempotency_key, raw=raw, organization=organization, acting_key=acting_key)

    def get_enrollments(self, *, query: Optional[Mapping[str, Any]] = None, idempotency_key: Optional[str] = None, raw: bool = False, organization: Optional[str] = None, acting_key: Optional[str] = None) -> Any:
        """List your enrollments: GET /v1/enrollments

        Newest first, each with its status, whose turn it is and its open tasks. Filter with status,
        transaction, payer_id, provider_id, npi, waiting_on and the created and updated dates. A
        page may hold fewer than limit, so keep paging while has_more is true.

        query: limit (How many a page holds, 1 to 100.); starting_after (The next_cursor of the page
        before, or the id of a enrollment on it.); status (One or more statuses, separated by
        commas: draft, payerworks_action_required, provider_action_required, with_payer, live,
        rejected, canceled, ended.); transaction; payer_id; provider_id; npi; waiting_on (Only
        requests that are this party’s turn.); created_after (A day (YYYY-MM-DD, its 00:00 UTC) or
        an instant; inclusive.); created_before (A day (YYYY-MM-DD, its 00:00 UTC) or an instant;
        exclusive.); updated_after (A day (YYYY-MM-DD, its 00:00 UTC) or an instant; inclusive.);
        updated_before (A day (YYYY-MM-DD, its 00:00 UTC) or an instant; exclusive.).
        """
        return self.request("GET", "/v1/enrollments", query=query, idempotency_key=idempotency_key, raw=raw, organization=organization, acting_key=acting_key)

    def get_enrollments_by_id(self, id: str, *, query: Optional[Mapping[str, Any]] = None, idempotency_key: Optional[str] = None, raw: bool = False, organization: Optional[str] = None, acting_key: Optional[str] = None) -> Any:
        """One enrollment: GET /v1/enrollments/{id}

        Its status and whose turn it is, in words; its tasks in rank order, its documents, notes
        from Payerworks, a rejection with its remedy, and the history of every status change.
        """
        return self.request("GET", "/v1/enrollments/" + _segment(id, "id"), query=query, idempotency_key=idempotency_key, raw=raw, organization=organization, acting_key=acting_key)

    def post_enrollments_by_id(self, id: str, body: Dict[str, Any], *, query: Optional[Mapping[str, Any]] = None, idempotency_key: Optional[str] = None, raw: bool = False, organization: Optional[str] = None, acting_key: Optional[str] = None) -> Any:
        """Change an enrollment: POST /v1/enrollments/{id}

        The contact, the notice email, the effective date, the remittance grouping, the PTAN or your
        note, until the request is sent to a payer; while it is a draft, the payer and the
        transaction too.
        """
        return self.request("POST", "/v1/enrollments/" + _segment(id, "id"), body=body, query=query, idempotency_key=idempotency_key, raw=raw, organization=organization, acting_key=acting_key)

    def delete_enrollments_by_id(self, id: str, *, query: Optional[Mapping[str, Any]] = None, idempotency_key: Optional[str] = None, raw: bool = False, organization: Optional[str] = None, acting_key: Optional[str] = None) -> Any:
        """Delete an enrollment: DELETE /v1/enrollments/{id}

        A draft, a canceled or a rejected request. Deleting again answers the same.
        """
        return self.request("DELETE", "/v1/enrollments/" + _segment(id, "id"), query=query, idempotency_key=idempotency_key, raw=raw, organization=organization, acting_key=acting_key)

    def post_enrollments_by_id_submit(self, id: str, body: Optional[Dict[str, Any]] = None, *, query: Optional[Mapping[str, Any]] = None, idempotency_key: Optional[str] = None, raw: bool = False, organization: Optional[str] = None, acting_key: Optional[str] = None) -> Any:
        """Submit a draft: POST /v1/enrollments/{id}/submit

        The draft is checked again and handed to Payerworks.
        """
        return self.request("POST", "/v1/enrollments/" + _segment(id, "id") + "/submit", body=body, query=query, idempotency_key=idempotency_key, raw=raw, organization=organization, acting_key=acting_key)

    def post_enrollments_by_id_cancel(self, id: str, body: Dict[str, Any], *, query: Optional[Mapping[str, Any]] = None, idempotency_key: Optional[str] = None, raw: bool = False, organization: Optional[str] = None, acting_key: Optional[str] = None) -> Any:
        """Cancel an enrollment: POST /v1/enrollments/{id}/cancel

        In any open status. Before it is sent to a payer it is canceled at once; once it is with a
        payer, or live, the answer is 202 and Payerworks withdraws it.
        """
        return self.request("POST", "/v1/enrollments/" + _segment(id, "id") + "/cancel", body=body, query=query, idempotency_key=idempotency_key, raw=raw, organization=organization, acting_key=acting_key)

    def post_tasks_by_id(self, id: str, body: Dict[str, Any], *, query: Optional[Mapping[str, Any]] = None, idempotency_key: Optional[str] = None, raw: bool = False, organization: Optional[str] = None, acting_key: Optional[str] = None) -> Any:
        """Answer a task: POST /v1/tasks/{id}

        Save a task’s values, text or an uploaded document, and finish it with complete: true, in
        rank order. A task held up by an earlier one is refused naming that task, its rank and who
        holds it, Payerworks included. complete: false reopens a task finished by mistake while
        nothing has gone to a payer.
        """
        return self.request("POST", "/v1/tasks/" + _segment(id, "id"), body=body, query=query, idempotency_key=idempotency_key, raw=raw, organization=organization, acting_key=acting_key)

    def post_enrollments_by_id_documents(self, id: str, body: Dict[str, Any], *, query: Optional[Mapping[str, Any]] = None, idempotency_key: Optional[str] = None, raw: bool = False, organization: Optional[str] = None, acting_key: Optional[str] = None) -> Any:
        """Upload a PDF for a task: POST /v1/enrollments/{id}/documents

        A signed upload address for one PDF of up to 10 MB, good for 15 minutes. Post the file
        there, then confirm it, or finish the task with its document_id, which confirms it too.
        """
        return self.request("POST", "/v1/enrollments/" + _segment(id, "id") + "/documents", body=body, query=query, idempotency_key=idempotency_key, raw=raw, organization=organization, acting_key=acting_key)

    def post_documents_by_id_confirm(self, id: str, body: Optional[Dict[str, Any]] = None, *, query: Optional[Mapping[str, Any]] = None, idempotency_key: Optional[str] = None, raw: bool = False, organization: Optional[str] = None, acting_key: Optional[str] = None) -> Any:
        """Confirm an upload: POST /v1/documents/{id}/confirm

        Checks that the file arrived and is a PDF of up to 10 MB. One that is not is deleted, and
        the document is marked failed.
        """
        return self.request("POST", "/v1/documents/" + _segment(id, "id") + "/confirm", body=body, query=query, idempotency_key=idempotency_key, raw=raw, organization=organization, acting_key=acting_key)

    def get_documents_by_id(self, id: str, *, query: Optional[Mapping[str, Any]] = None, idempotency_key: Optional[str] = None, raw: bool = False, organization: Optional[str] = None, acting_key: Optional[str] = None) -> Any:
        """Download a document: GET /v1/documents/{id}

        A signed download address, good for 15 minutes, for a PDF you uploaded or a form Payerworks
        attached to a task.
        """
        return self.request("GET", "/v1/documents/" + _segment(id, "id"), query=query, idempotency_key=idempotency_key, raw=raw, organization=organization, acting_key=acting_key)

    def delete_documents_by_id(self, id: str, *, query: Optional[Mapping[str, Any]] = None, idempotency_key: Optional[str] = None, raw: bool = False, organization: Optional[str] = None, acting_key: Optional[str] = None) -> Any:
        """Delete a document: DELETE /v1/documents/{id}

        Refused while a finished task names it: reopen the task first. Deleting again answers the
        same.
        """
        return self.request("DELETE", "/v1/documents/" + _segment(id, "id"), query=query, idempotency_key=idempotency_key, raw=raw, organization=organization, acting_key=acting_key)

    def get_enrollment_requirements(self, *, query: Optional[Mapping[str, Any]] = None, idempotency_key: Optional[str] = None, raw: bool = False, organization: Optional[str] = None, acting_key: Optional[str] = None) -> Any:
        """Payers with published steps: GET /v1/enrollment-requirements

        Every payer Payerworks holds published enrollment steps for, the four Medicare DME MACs and
        CMS’s HETS, and the sandbox payer: per transaction, who files, the fields needed and each
        step, with its source and the day it was read.
        """
        return self.request("GET", "/v1/enrollment-requirements", query=query, idempotency_key=idempotency_key, raw=raw, organization=organization, acting_key=acting_key)

    def get_enrollment_requirements_by_payer_id(self, payer_id: str, *, query: Optional[Mapping[str, Any]] = None, idempotency_key: Optional[str] = None, raw: bool = False, organization: Optional[str] = None, acting_key: Optional[str] = None) -> Any:
        """What a payer asks: GET /v1/enrollment-requirements/{payer_id}

        For any payer id, per transaction: whether Payerworks’ route is open, who files, the fields
        needed and each published step with its source. A payer Payerworks holds nothing for answers
        published: false, never not required. Add ?transaction= for one.

        query: transaction (Only this transaction.).
        """
        return self.request("GET", "/v1/enrollment-requirements/" + _segment(payer_id, "payer_id"), query=query, idempotency_key=idempotency_key, raw=raw, organization=organization, acting_key=acting_key)

    def post_sandbox_enrollments_by_id_advance(self, id: str, body: Optional[Dict[str, Any]] = None, *, query: Optional[Mapping[str, Any]] = None, idempotency_key: Optional[str] = None, raw: bool = False, organization: Optional[str] = None, acting_key: Optional[str] = None) -> Any:
        """Advance a sandbox enrollment: POST /v1/sandbox/enrollments/{id}/advance

        The sandbox payer’s next move on a test request, with the events it raised. A test key only;
        the last word of the provider’s name picks the scenario.
        """
        return self.request("POST", "/v1/sandbox/enrollments/" + _segment(id, "id") + "/advance", body=body, query=query, idempotency_key=idempotency_key, raw=raw, organization=organization, acting_key=acting_key)

    def post_enrollments_import_check(self, body: Dict[str, Any], *, query: Optional[Mapping[str, Any]] = None, idempotency_key: Optional[str] = None, raw: bool = False, organization: Optional[str] = None, acting_key: Optional[str] = None) -> Any:
        """Check an enrollment import: POST /v1/enrollments/import/check

        Send { csv } with the template’s header and up to 100 rows, one enrollment request a row.
        Each row is checked exactly as POST /v1/providers and POST /v1/enrollments check their
        bodies, each problem named by its column, and said as ready, invalid or duplicate (a request
        that already stands, or an earlier row asking the same). With a live key, each NPI is looked
        up in the NPI registry. Nothing is saved.
        """
        return self.request("POST", "/v1/enrollments/import/check", body=body, query=query, idempotency_key=idempotency_key, raw=raw, organization=organization, acting_key=acting_key)

    def post_enrollments_import(self, body: Dict[str, Any], *, query: Optional[Mapping[str, Any]] = None, idempotency_key: Optional[str] = None, raw: bool = False, organization: Optional[str] = None, acting_key: Optional[str] = None) -> Any:
        """Import enrollment requests: POST /v1/enrollments/import

        The same file, each valid row made into one request, and its provider when your key has none
        with that NPI and tax id. A provider you already have is used as it is, never changed. Each
        row answers created, invalid, duplicate, refused or not_run; rows not started in time are
        not_run from not_run_from_row, so send those again. A row is submitted only when its submit
        cell says true.
        """
        return self.request("POST", "/v1/enrollments/import", body=body, query=query, idempotency_key=idempotency_key, raw=raw, organization=organization, acting_key=acting_key)

    def get_enrollments_export(self, *, query: Optional[Mapping[str, Any]] = None, idempotency_key: Optional[str] = None, raw: bool = False, organization: Optional[str] = None, acting_key: Optional[str] = None) -> Any:
        """Export your enrollments: GET /v1/enrollments/export

        Every request matching the list’s filters, as CSV text in the answer, up to 5,000 rows:
        status, whose turn, provider, payer, transaction and the dates. No contact, note or full tax
        id. A cell a spreadsheet would run as a formula starts with an apostrophe. It takes no limit
        or starting_after.

        query: status (One or more statuses, separated by commas: draft, payerworks_action_required,
        provider_action_required, with_payer, live, rejected, canceled, ended.); transaction;
        payer_id; provider_id; npi; waiting_on (Only requests that are this party’s turn.);
        created_after (A day (YYYY-MM-DD, its 00:00 UTC) or an instant; inclusive.); created_before
        (A day (YYYY-MM-DD, its 00:00 UTC) or an instant; exclusive.); updated_after (A day
        (YYYY-MM-DD, its 00:00 UTC) or an instant; inclusive.); updated_before (A day (YYYY-MM-DD,
        its 00:00 UTC) or an instant; exclusive.).
        """
        return self.request("GET", "/v1/enrollments/export", query=query, idempotency_key=idempotency_key, raw=raw, organization=organization, acting_key=acting_key)

    def get_enrollments_durations(self, *, query: Optional[Mapping[str, Any]] = None, idempotency_key: Optional[str] = None, raw: bool = False, organization: Optional[str] = None, acting_key: Optional[str] = None) -> Any:
        """How long your requests took: GET /v1/enrollments/durations

        By payer and transaction, how long your own requests spent waiting on Payerworks, on the
        provider and with the payer, and from submitted to live: how many finished, and once at
        least five did, the median and the middle half in hours. Fewer than five says not enough
        history. From your key’s own history only: Payerworks has no other figures.

        query: payer_id; transaction; provider_id.
        """
        return self.request("GET", "/v1/enrollments/durations", query=query, idempotency_key=idempotency_key, raw=raw, organization=organization, acting_key=acting_key)

    def get_payers(self, *, query: Optional[Mapping[str, Any]] = None, idempotency_key: Optional[str] = None, raw: bool = False, organization: Optional[str] = None, acting_key: Optional[str] = None) -> Any:
        """Search the payer directory: GET /v1/payers

        Payers and plan programs from public federal files (Medicare Advantage and Part D contracts,
        Medicaid managed care plans, insurers’ loss ratio filings, employer plans’ Form 5500
        carriers) and the Medicare contractors; not a list Payerworks can send to; a payer ID only
        where the payer publishes it, with the page and the day it was read. Search with q by name,
        a misspelling, a payer ID, a NAIC code or a CMS contract ID (a state named in q is read as
        state=); narrow with state and type; page with limit (up to 100) and starting_after. Every
        row says connection none, and an all-digit payer ID under five digits is warned about:
        leading zeros are part of it.

        query: q (A name, a misspelling, a payer ID, a NAIC code, a CMS contract ID or a directory
        row id. A state named in it (“texas medicaid”, “medicare part b TX”) is read as state= when
        none is sent and the whole text names no row. Left out: every row, in order.); state (Two
        letters: only rows serving that state, each with payer_ids_for_state and state_match. A row
        whose area is not stated is kept only by its legal entity’s state, or as a payer’s own page
        row, and says which.); type; limit; starting_after (The next_cursor of the page before, in
        the same search.).
        """
        return self.request("GET", "/v1/payers", query=query, idempotency_key=idempotency_key, raw=raw, organization=organization, acting_key=acting_key)

    def get_payers_by_id(self, id: str, *, query: Optional[Mapping[str, Any]] = None, idempotency_key: Optional[str] = None, raw: bool = False, organization: Optional[str] = None, acting_key: Optional[str] = None) -> Any:
        """One directory row: GET /v1/payers/{id}

        One row in full by its directory id (pwd_ and 16 hex): its names, where its sources say it
        serves (or Not stated), programs, the payer IDs it publishes with their sources, and the
        files it came from. A payer ID sent here is refused 400 not_a_directory_row_id and pointed
        to the search, never answered as not found.
        """
        return self.request("GET", "/v1/payers/" + _segment(id, "id"), query=query, idempotency_key=idempotency_key, raw=raw, organization=organization, acting_key=acting_key)

    def post_payers_from_card(self, body: Dict[str, Any], *, query: Optional[Mapping[str, Any]] = None, idempotency_key: Optional[str] = None, raw: bool = False, organization: Optional[str] = None, acting_key: Optional[str] = None) -> Any:
        """Look up a payer from a card photo: POST /v1/payers/from-card

        A photo of an insurance card’s front (and back, if you have it), read twice by a model for
        the payer’s name, the member ID and its shape, the group number and the plan type the card
        itself states, and the payer looked up in the Payerworks payer directory. A match means the
        directory holds published facts about that payer, never that a claim can be sent to it; an
        electronic payer ID printed on the card is shown only where the payer publishes it. Off
        until Payerworks turns it on; test keys and invented cards only for now, at most 100 reads a
        UTC day for an account’s keys together; a call refused for the route being off, a live key
        or no file is not counted. A read has 25 seconds; past them the answer is 504
        model_read_timed_out: send one page, or a crop of the part you need. Nothing is kept.
        """
        return self.request("POST", "/v1/payers/from-card", body=body, query=query, idempotency_key=idempotency_key, raw=raw, organization=organization, acting_key=acting_key)

    def get_codes_carc_by_code(self, code: str, *, query: Optional[Mapping[str, Any]] = None, idempotency_key: Optional[str] = None, raw: bool = False, organization: Optional[str] = None, acting_key: Optional[str] = None) -> Any:
        """An adjustment reason code: GET /v1/codes/carc/{code}

        A claim adjustment reason code in words, with its category, sent bare (45) or with its group
        (CO-45). A remark code sent here is named as one.
        """
        return self.request("GET", "/v1/codes/carc/" + _segment(code, "code"), query=query, idempotency_key=idempotency_key, raw=raw, organization=organization, acting_key=acting_key)

    def get_codes_group_by_code(self, code: str, *, query: Optional[Mapping[str, Any]] = None, idempotency_key: Optional[str] = None, raw: bool = False, organization: Optional[str] = None, acting_key: Optional[str] = None) -> Any:
        """An adjustment group code: GET /v1/codes/group/{code}

        A claim adjustment group code (CO, PR, OA, PI or CR) in words, and whether the patient may
        be billed an amount under it.
        """
        return self.request("GET", "/v1/codes/group/" + _segment(code, "code"), query=query, idempotency_key=idempotency_key, raw=raw, organization=organization, acting_key=acting_key)

    def get_codes_claim_status_by_category(self, category: str, *, query: Optional[Mapping[str, Any]] = None, idempotency_key: Optional[str] = None, raw: bool = False, organization: Optional[str] = None, acting_key: Optional[str] = None) -> Any:
        """A claim status category: GET /v1/codes/claim-status/{category}

        A claim status category code in words, read both ways: on a claim acknowledgment (277CA),
        whether it means accepted, rejected or denied, and in a claim status answer, the state the
        explainer gives it.
        """
        return self.request("GET", "/v1/codes/claim-status/" + _segment(category, "category"), query=query, idempotency_key=idempotency_key, raw=raw, organization=organization, acting_key=acting_key)

    def get_codes_claim_status_code_by_code(self, code: str, *, query: Optional[Mapping[str, Any]] = None, idempotency_key: Optional[str] = None, raw: bool = False, organization: Optional[str] = None, acting_key: Optional[str] = None) -> Any:
        """A claim status code: GET /v1/codes/claim-status-code/{code}

        A claim status code in words, and whether it names an entity.
        """
        return self.request("GET", "/v1/codes/claim-status-code/" + _segment(code, "code"), query=query, idempotency_key=idempotency_key, raw=raw, organization=organization, acting_key=acting_key)

    def get_codes_service_type_by_code(self, code: str, *, query: Optional[Mapping[str, Any]] = None, idempotency_key: Optional[str] = None, raw: bool = False, organization: Optional[str] = None, acting_key: Optional[str] = None) -> Any:
        """A service type code: GET /v1/codes/service-type/{code}

        A service type code in words: its 005010 meaning where Payerworks has verified it, and the
        meaning later X12 versions gave a code they reassigned.
        """
        return self.request("GET", "/v1/codes/service-type/" + _segment(code, "code"), query=query, idempotency_key=idempotency_key, raw=raw, organization=organization, acting_key=acting_key)

    def get_codes_aaa_by_code(self, code: str, *, query: Optional[Mapping[str, Any]] = None, idempotency_key: Optional[str] = None, raw: bool = False, organization: Optional[str] = None, acting_key: Optional[str] = None) -> Any:
        """A reject reason code: GET /v1/codes/aaa/{code}

        A reject reason from a 271 (its AAA segment) in words, whether asking again can help, and
        the request fields to correct. Add ?loop=2100D for a dependent.

        query: loop (The loop the reason was returned in, such as 2100C or 2100D (a dependent).).
        """
        return self.request("GET", "/v1/codes/aaa/" + _segment(code, "code"), query=query, idempotency_key=idempotency_key, raw=raw, organization=organization, acting_key=acting_key)

    def post_sandbox_claims(self, body: Dict[str, Any], *, query: Optional[Mapping[str, Any]] = None, idempotency_key: Optional[str] = None, raw: bool = False, organization: Optional[str] = None, acting_key: Optional[str] = None) -> Any:
        """Run the sandbox payer: POST /v1/sandbox/claims

        Your claims written as an 837P and answered by the sandbox payer in the same call: a 999 for
        the file, a 277CA for each claim and an 835 for each claim it adjudicated, every file
        explained, and claims[] with each claim’s warnings, its repairs and the checks that could
        not run. Any key may call it, every file is a test file, and nothing reaches a payer. How
        the member ID ends decides the answer. A member ID ending in RFAI is held for a document: a
        277 request for additional information follows. Send the same claims again with the
        solicited 275 (attachments_275, at most five) and the claim is paid.
        """
        return self.request("POST", "/v1/sandbox/claims", body=body, query=query, idempotency_key=idempotency_key, raw=raw, organization=organization, acting_key=acting_key)

    def post_sandbox_claims_837i(self, body: Dict[str, Any], *, query: Optional[Mapping[str, Any]] = None, idempotency_key: Optional[str] = None, raw: bool = False, organization: Optional[str] = None, acting_key: Optional[str] = None) -> Any:
        """Run the sandbox payer on an 837I: POST /v1/sandbox/claims/837i

        Your institutional claims checked as POST /v1/claims/837i checks them, written as an 837I
        and answered by the same sandbox payer as the professional claims, by the same rules: how
        the member ID ends decides the answer, and the paid amounts and payer claim numbers are made
        the same way. The answer is a 999 for the file, a 277CA for each claim and an 835 for each
        claim it adjudicated, and explain_with names the routes that explain them. A member ID
        ending in RFAI is held for a document, as on POST /v1/sandbox/claims: send the same claims
        again with the solicited 275 in attachments_275 and the claim is paid. Any key may call it,
        every file is a test file, nothing is posted to your webhook, and nothing reaches a payer.
        """
        return self.request("POST", "/v1/sandbox/claims/837i", body=body, query=query, idempotency_key=idempotency_key, raw=raw, organization=organization, acting_key=acting_key)

    def post_sandbox_claims_837d(self, body: Dict[str, Any], *, query: Optional[Mapping[str, Any]] = None, idempotency_key: Optional[str] = None, raw: bool = False, organization: Optional[str] = None, acting_key: Optional[str] = None) -> Any:
        """Run the sandbox payer on an 837D: POST /v1/sandbox/claims/837d

        Your dental claims and predeterminations, checked as POST /v1/claims/837d checks them and
        answered by the same sandbox payer, by the same member ID endings, with a 999, a 277CA and
        an 835. A predetermination is answered as one: CLP02 25, nothing paid, each line’s would-be
        payment as an OA 101 adjustment, and the estimate in the outcome. A member ID ending in RFAI
        is held for a document: send the same claims again with the solicited 275 in attachments_275
        and the claim is paid. Any key may call it, every file is a test file, nothing is posted to
        your webhook, and nothing reaches a payer.
        """
        return self.request("POST", "/v1/sandbox/claims/837d", body=body, query=query, idempotency_key=idempotency_key, raw=raw, organization=organization, acting_key=acting_key)

    def post_sandbox_eligibility(self, body: Dict[str, Any], *, query: Optional[Mapping[str, Any]] = None, idempotency_key: Optional[str] = None, raw: bool = False, organization: Optional[str] = None, acting_key: Optional[str] = None) -> Any:
        """Run the eligibility sandbox: POST /v1/sandbox/eligibility

        Your eligibility questions written as a 270 and answered by the sandbox payer with a 271 in
        the same call, every answer explained. Any key may call it, every file is a test file, and
        how the member ID ends decides the answer.
        """
        return self.request("POST", "/v1/sandbox/eligibility", body=body, query=query, idempotency_key=idempotency_key, raw=raw, organization=organization, acting_key=acting_key)

    def post_sandbox_claim_status(self, body: Dict[str, Any], *, query: Optional[Mapping[str, Any]] = None, idempotency_key: Optional[str] = None, raw: bool = False, organization: Optional[str] = None, acting_key: Optional[str] = None) -> Any:
        """Run the claim status sandbox: POST /v1/sandbox/claim-status

        Your claim status questions, as JSON or a 276, answered by the sandbox payer with a 277 in
        the same call and explained. It agrees with the claims sandbox: the same payer claim number
        and, for each claim, the same paid amount. How the member ID ends decides the answer.
        """
        return self.request("POST", "/v1/sandbox/claim-status", body=body, query=query, idempotency_key=idempotency_key, raw=raw, organization=organization, acting_key=acting_key)

    def post_sandbox_claims_timeline(self, body: Dict[str, Any], *, query: Optional[Mapping[str, Any]] = None, idempotency_key: Optional[str] = None, raw: bool = False, organization: Optional[str] = None, acting_key: Optional[str] = None) -> Any:
        """Run the sandbox over days: POST /v1/sandbox/claims/timeline

        The claims sandbox spread over simulated time: the 999 after five minutes, a 277CA saying
        each claim was received after an hour, the payer’s own 277CA after two days and the 835
        after fourteen. Send the claims, a seed, the start and the moment to read as of; send a
        later moment and the cursor you were given to read what came next. Each event is the exact
        body a webhook sends, so you can post it to your own handler; Payerworks posts none of them.
        Add format: report for the 277CAs and 835 in the JSON field names most clearinghouse APIs
        use. Nothing is stored or sent: the same request gives the same answer.
        """
        return self.request("POST", "/v1/sandbox/claims/timeline", body=body, query=query, idempotency_key=idempotency_key, raw=raw, organization=organization, acting_key=acting_key)

    def get_account(self, *, query: Optional[Mapping[str, Any]] = None, idempotency_key: Optional[str] = None, raw: bool = False, organization: Optional[str] = None, acting_key: Optional[str] = None) -> Any:
        """Your account: GET /v1/account

        The key’s account, label, mode and rate, the calls it made on each day of this month
        (counted by UTC day) and the calls refused before they ran, and its webhook if one is set,
        with what its last delivery did.
        """
        return self.request("GET", "/v1/account", query=query, idempotency_key=idempotency_key, raw=raw, organization=organization, acting_key=acting_key)

    def put_account_webhook(self, body: Dict[str, Any], *, query: Optional[Mapping[str, Any]] = None, idempotency_key: Optional[str] = None, raw: bool = False, organization: Optional[str] = None, acting_key: Optional[str] = None) -> Any:
        """Set your webhook: PUT /v1/account/webhook

        The https address Payerworks posts your events to. The first answer carries the signing
        secret, once. Setting the address again keeps the secret; send rotate: true for a new one,
        and the previous secret keeps signing beside it for overlap_hours (0 to 720, 24 by default).
        """
        return self.request("PUT", "/v1/account/webhook", body=body, query=query, idempotency_key=idempotency_key, raw=raw, organization=organization, acting_key=acting_key)

    def delete_account_webhook(self, *, query: Optional[Mapping[str, Any]] = None, idempotency_key: Optional[str] = None, raw: bool = False, organization: Optional[str] = None, acting_key: Optional[str] = None) -> Any:
        """Remove your webhook: DELETE /v1/account/webhook

        No more events are sent. Removing a webhook that is not there is not an error.
        """
        return self.request("DELETE", "/v1/account/webhook", query=query, idempotency_key=idempotency_key, raw=raw, organization=organization, acting_key=acting_key)

    def post_account_webhook_test(self, body: Optional[Dict[str, Any]] = None, *, query: Optional[Mapping[str, Any]] = None, idempotency_key: Optional[str] = None, raw: bool = False, organization: Optional[str] = None, acting_key: Optional[str] = None) -> Any:
        """Send a test event: POST /v1/account/webhook/test

        One ping event queued for your webhook, signed like every other. The answer is 202 with the
        event’s id; GET /v1/events/{id} says a moment later what your server did with it.
        """
        return self.request("POST", "/v1/account/webhook/test", body=body, query=query, idempotency_key=idempotency_key, raw=raw, organization=organization, acting_key=acting_key)

    def get_account_contact(self, *, query: Optional[Mapping[str, Any]] = None, idempotency_key: Optional[str] = None, raw: bool = False, organization: Optional[str] = None, acting_key: Optional[str] = None) -> Any:
        """Your notice address: GET /v1/account/contact

        Where Payerworks emails you about this key: when one of its webhook destinations keeps
        failing, and when one of its enrollment requests moves without your doing it. A test key
        from this page starts with the address its link was sent to, the only address it may set.
        """
        return self.request("GET", "/v1/account/contact", query=query, idempotency_key=idempotency_key, raw=raw, organization=organization, acting_key=acting_key)

    def put_account_contact(self, body: Dict[str, Any], *, query: Optional[Mapping[str, Any]] = None, idempotency_key: Optional[str] = None, raw: bool = False, organization: Optional[str] = None, acting_key: Optional[str] = None) -> Any:
        """Set your notice address: PUT /v1/account/contact

        The address told when a destination has failed for 2 hours, for 24 hours, and when it was
        disabled after its last attempt, and sent one email for every enrollment request that moved
        without your doing it inside the same 15 minutes. Send email: null to remove it, and nothing
        is emailed. Only the address that asked for this account’s test key by email (any key of the
        account may set it), or null; any other address is refused 403 contact_not_proved. A key
        issued by Payerworks has its address set by us: write to us.
        """
        return self.request("PUT", "/v1/account/contact", body=body, query=query, idempotency_key=idempotency_key, raw=raw, organization=organization, acting_key=acting_key)

    def get_keys(self, *, query: Optional[Mapping[str, Any]] = None, idempotency_key: Optional[str] = None, raw: bool = False, organization: Optional[str] = None, acting_key: Optional[str] = None) -> Any:
        """Your account’s keys: GET /v1/keys

        Every key of your account in your key’s own mode (a test key lists the test keys, a live key
        the live ones): its name, mode, last four characters, when it was made, when it expires,
        when it was last rotated, until when its previous secret is accepted, whether it has
        expired, when it was revoked, and which one is the key asking, beside the account’s id. A
        revoked key is listed too. Never a key itself.
        """
        return self.request("GET", "/v1/keys", query=query, idempotency_key=idempotency_key, raw=raw, organization=organization, acting_key=acting_key)

    def post_keys(self, body: Dict[str, Any], *, query: Optional[Mapping[str, Any]] = None, idempotency_key: Optional[str] = None, raw: bool = False, organization: Optional[str] = None, acting_key: Optional[str] = None) -> Any:
        """Make a key: POST /v1/keys

        A named key of your key’s own mode and rate, shown once. expires_in_days (1 to 365) makes it
        stop on its own. A key never outlives the key that made it: its expiry is the sooner of
        expires_in_days and the making key’s own, and the making key’s when none is asked. An
        account holds 3 test keys or 25 live ones that are not revoked. Every key of an account may
        manage its keys, unless the account’s organization chose members only.
        """
        return self.request("POST", "/v1/keys", body=body, query=query, idempotency_key=idempotency_key, raw=raw, organization=organization, acting_key=acting_key)

    def delete_keys_by_id(self, id: str, *, query: Optional[Mapping[str, Any]] = None, idempotency_key: Optional[str] = None, raw: bool = False, organization: Optional[str] = None, acting_key: Optional[str] = None) -> Any:
        """Revoke a key: DELETE /v1/keys/{id}

        The key stops working within a minute, and its SFTP user, if it has one, is removed at once.
        Revoking a key that is already revoked is not an error.
        """
        return self.request("DELETE", "/v1/keys/" + _segment(id, "id"), query=query, idempotency_key=idempotency_key, raw=raw, organization=organization, acting_key=acting_key)

    def post_keys_by_id_rotate(self, id: str, body: Optional[Dict[str, Any]] = None, *, query: Optional[Mapping[str, Any]] = None, idempotency_key: Optional[str] = None, raw: bool = False, organization: Optional[str] = None, acting_key: Optional[str] = None) -> Any:
        """Rotate a key: POST /v1/keys/{id}/rotate

        A new secret for the same key id, shown once, so its webhooks, events, usage and enrollments
        carry on. The old secret is still accepted for overlap_hours, 0 to 720, 24 by default, and
        stops at once if the key is revoked. Two rotations at once: the first finishes, and the
        other answers 409 key_rotation_conflict and makes nothing.
        """
        return self.request("POST", "/v1/keys/" + _segment(id, "id") + "/rotate", body=body, query=query, idempotency_key=idempotency_key, raw=raw, organization=organization, acting_key=acting_key)

    def post_mcp(self, body: Dict[str, Any], *, query: Optional[Mapping[str, Any]] = None, idempotency_key: Optional[str] = None, raw: bool = False, organization: Optional[str] = None, acting_key: Optional[str] = None) -> Any:
        """The MCP server, hosted: POST /v1/mcp

        The same tools as the downloadable MCP server, over MCP’s Streamable HTTP transport
        (2026-07-28, and the 2025 handshakes), with your key as the bearer. Each tool call is a call
        to its route under your key. With a live key no tool is called unless the request carries
        Payerworks-MCP-Allow-Live-Key: 1. Payerworks transmits nothing.
        """
        return self.request("POST", "/v1/mcp", body=body, query=query, idempotency_key=idempotency_key, raw=raw, organization=organization, acting_key=acting_key)

    def get_events(self, *, query: Optional[Mapping[str, Any]] = None, idempotency_key: Optional[str] = None, raw: bool = False, organization: Optional[str] = None, acting_key: Optional[str] = None) -> Any:
        """List your events: GET /v1/events

        Every webhook event queued for your key that is still kept (each is kept 30 days), newest
        first, each as it was sent with what delivering it did. Page with limit (up to 100) and
        starting_after (the next_cursor of the page before, or an event id), and filter with type,
        delivery_state (none: recorded for your webhook destinations only, with no account webhook
        set) and created_gte and created_lt.

        query: limit (How many events a page holds, 1 to 100.); type (Only events of this type.);
        starting_after (The next_cursor of the page before, or the id of one of this key’s events.);
        delivery_state (Only events whose delivery says this; none: recorded for your webhook
        destinations only.); created_gte (Only those made at or after this instant, such as
        2026-10-19T14:00:00Z.); created_lt (Only those made before this instant.).
        """
        return self.request("GET", "/v1/events", query=query, idempotency_key=idempotency_key, raw=raw, organization=organization, acting_key=acting_key)

    def get_events_by_id(self, id: str, *, query: Optional[Mapping[str, Any]] = None, idempotency_key: Optional[str] = None, raw: bool = False, organization: Optional[str] = None, acting_key: Optional[str] = None) -> Any:
        """One event: GET /v1/events/{id}

        One event by its id, as it was sent, with its delivery: pending, delivered or failed, how
        many times it was posted, the last status and the last error.
        """
        return self.request("GET", "/v1/events/" + _segment(id, "id"), query=query, idempotency_key=idempotency_key, raw=raw, organization=organization, acting_key=acting_key)

    def post_events_by_id_redeliver(self, id: str, body: Optional[Dict[str, Any]] = None, *, query: Optional[Mapping[str, Any]] = None, idempotency_key: Optional[str] = None, raw: bool = False, organization: Optional[str] = None, acting_key: Optional[str] = None) -> Any:
        """Send an event again: POST /v1/events/{id}/redeliver

        The event queued once more for the address your webhook has now, signed the same way and
        with the same id. The answer is 202, and the event’s delivery then says what your server
        did. It counts against your key’s six deliveries a minute. Send destination_id to send it to
        that webhook destination instead, as a manual attempt that starts its retries again from the
        first.
        """
        return self.request("POST", "/v1/events/" + _segment(id, "id") + "/redeliver", body=body, query=query, idempotency_key=idempotency_key, raw=raw, organization=organization, acting_key=acting_key)

    def get_events_by_id_attempts(self, id: str, *, query: Optional[Mapping[str, Any]] = None, idempotency_key: Optional[str] = None, raw: bool = False, organization: Optional[str] = None, acting_key: Optional[str] = None) -> Any:
        """Every attempt for an event: GET /v1/events/{id}/attempts

        Each post made for one event at your webhook destinations: its number, automatic or manual,
        the status your server answered and when. destination_id narrows it to one.

        query: destination_id (Only the attempts at this destination.).
        """
        return self.request("GET", "/v1/events/" + _segment(id, "id") + "/attempts", query=query, idempotency_key=idempotency_key, raw=raw, organization=organization, acting_key=acting_key)

    def get_webhook_destinations(self, *, query: Optional[Mapping[str, Any]] = None, idempotency_key: Optional[str] = None, raw: bool = False, organization: Optional[str] = None, acting_key: Optional[str] = None) -> Any:
        """List your webhook destinations: GET /v1/webhook-destinations

        Every destination your key posts events to, besides your account webhook: up to ten, each
        with its name, address, the event types it takes, whether it is enabled, how many deliveries
        it takes at once, and the last four characters of its secret.
        """
        return self.request("GET", "/v1/webhook-destinations", query=query, idempotency_key=idempotency_key, raw=raw, organization=organization, acting_key=acting_key)

    def post_webhook_destinations(self, body: Dict[str, Any], *, query: Optional[Mapping[str, Any]] = None, idempotency_key: Optional[str] = None, raw: bool = False, organization: Optional[str] = None, acting_key: Optional[str] = None) -> Any:
        """Add a webhook destination: POST /v1/webhook-destinations

        A named https address with its own signing secret, shown once in this answer. event_types
        narrows what it receives (leave it out for every type), enabled turns it off and on, and
        concurrency (1 to 10, 2 by default) is how many deliveries it takes at once. Only events
        Payerworks makes are sent. A delivery that fails is tried again 5 minutes, 30 minutes, 2
        hours, 8 hours, 24 hours and 48 hours later; after the seventh attempt fails, the
        destination is disabled.
        """
        return self.request("POST", "/v1/webhook-destinations", body=body, query=query, idempotency_key=idempotency_key, raw=raw, organization=organization, acting_key=acting_key)

    def get_webhook_destinations_by_id(self, id: str, *, query: Optional[Mapping[str, Any]] = None, idempotency_key: Optional[str] = None, raw: bool = False, organization: Optional[str] = None, acting_key: Optional[str] = None) -> Any:
        """One webhook destination: GET /v1/webhook-destinations/{id}

        One destination, with when it started failing and the notices sent about it. Never its
        secret.
        """
        return self.request("GET", "/v1/webhook-destinations/" + _segment(id, "id"), query=query, idempotency_key=idempotency_key, raw=raw, organization=organization, acting_key=acting_key)

    def put_webhook_destinations_by_id(self, id: str, body: Dict[str, Any], *, query: Optional[Mapping[str, Any]] = None, idempotency_key: Optional[str] = None, raw: bool = False, organization: Optional[str] = None, acting_key: Optional[str] = None) -> Any:
        """Change a webhook destination: PUT /v1/webhook-destinations/{id}

        Change any of its fields, turn it on again after it was disabled, or send rotate: true for a
        new secret (shown once); the old one keeps signing beside it for overlap_hours, 0 to 720, 24
        by default.
        """
        return self.request("PUT", "/v1/webhook-destinations/" + _segment(id, "id"), body=body, query=query, idempotency_key=idempotency_key, raw=raw, organization=organization, acting_key=acting_key)

    def delete_webhook_destinations_by_id(self, id: str, *, query: Optional[Mapping[str, Any]] = None, idempotency_key: Optional[str] = None, raw: bool = False, organization: Optional[str] = None, acting_key: Optional[str] = None) -> Any:
        """Remove a webhook destination: DELETE /v1/webhook-destinations/{id}

        No more events are sent to it, and its retries already scheduled are skipped. Removing one
        that is not there is not an error.
        """
        return self.request("DELETE", "/v1/webhook-destinations/" + _segment(id, "id"), query=query, idempotency_key=idempotency_key, raw=raw, organization=organization, acting_key=acting_key)

    def post_webhook_destinations_by_id_ping(self, id: str, body: Optional[Dict[str, Any]] = None, *, query: Optional[Mapping[str, Any]] = None, idempotency_key: Optional[str] = None, raw: bool = False, organization: Optional[str] = None, acting_key: Optional[str] = None) -> Any:
        """Ping a webhook destination: POST /v1/webhook-destinations/{id}/ping

        One signed ping event to this destination alone, whatever event types it takes. The answer
        is 202 with the event’s id.
        """
        return self.request("POST", "/v1/webhook-destinations/" + _segment(id, "id") + "/ping", body=body, query=query, idempotency_key=idempotency_key, raw=raw, organization=organization, acting_key=acting_key)

    def get_webhook_destinations_by_id_deliveries(self, id: str, *, query: Optional[Mapping[str, Any]] = None, idempotency_key: Optional[str] = None, raw: bool = False, organization: Optional[str] = None, acting_key: Optional[str] = None) -> Any:
        """What a destination received: GET /v1/webhook-destinations/{id}/deliveries

        Each event’s delivery at this destination, newest first: pending, scheduled (failed, with
        the next automatic attempt’s time), delivered or failed, how many attempts, the last status
        and error. Filter with state, created_gte and created_lt; page with limit and
        starting_after.

        query: limit; state; created_gte (Only those made at or after this instant, such as
        2026-10-19T14:00:00Z.); created_lt (Only those made before this instant.); starting_after
        (The event_id of the last delivery on the page before.).
        """
        return self.request("GET", "/v1/webhook-destinations/" + _segment(id, "id") + "/deliveries", query=query, idempotency_key=idempotency_key, raw=raw, organization=organization, acting_key=acting_key)

    def get_sftp(self, *, query: Optional[Mapping[str, Any]] = None, idempotency_key: Optional[str] = None, raw: bool = False, organization: Optional[str] = None, acting_key: Optional[str] = None) -> Any:
        """Your SFTP user: GET /v1/sftp

        The host, your user name, your two folders (/inbound and /outbound), your SSH keys by
        fingerprint, when a 999 is written, and what is kept and for how long. A key with no user
        answers 404 sftp_user_not_found.
        """
        return self.request("GET", "/v1/sftp", query=query, idempotency_key=idempotency_key, raw=raw, organization=organization, acting_key=acting_key)

    def put_sftp(self, body: Optional[Dict[str, Any]] = None, *, query: Optional[Mapping[str, Any]] = None, idempotency_key: Optional[str] = None, raw: bool = False, organization: Optional[str] = None, acting_key: Optional[str] = None) -> Any:
        """Make your SFTP user: PUT /v1/sftp

        Makes this key’s SFTP user, or changes when a 999 is written for a file you drop:
        acknowledgment_999 is always, errors_only (the default: only when a group or a set is not
        plainly accepted) or never. A test key’s user sends usage indicator T to receiver
        PAYERWORKSTEST; a live key’s sends P to PAYERWORKS (qualifier ZZ). Payerworks validates and
        acknowledges what you drop; nothing goes on to a payer.
        """
        return self.request("PUT", "/v1/sftp", body=body, query=query, idempotency_key=idempotency_key, raw=raw, organization=organization, acting_key=acting_key)

    def delete_sftp(self, *, query: Optional[Mapping[str, Any]] = None, idempotency_key: Optional[str] = None, raw: bool = False, organization: Optional[str] = None, acting_key: Optional[str] = None) -> Any:
        """Remove your SFTP user: DELETE /v1/sftp

        The user can no longer sign in. Answers already in /outbound are deleted by the bucket 7
        days after each was written.
        """
        return self.request("DELETE", "/v1/sftp", query=query, idempotency_key=idempotency_key, raw=raw, organization=organization, acting_key=acting_key)

    def post_sftp_ssh_keys(self, body: Dict[str, Any], *, query: Optional[Mapping[str, Any]] = None, idempotency_key: Optional[str] = None, raw: bool = False, organization: Optional[str] = None, acting_key: Optional[str] = None) -> Any:
        """Add an SSH key: POST /v1/sftp/ssh-keys

        One OpenSSH public key line (ssh-ed25519, ssh-rsa or ecdsa), as your .pub file holds it; at
        most 10. There are no passwords. A private key is refused and never kept; a key’s comment is
        dropped.
        """
        return self.request("POST", "/v1/sftp/ssh-keys", body=body, query=query, idempotency_key=idempotency_key, raw=raw, organization=organization, acting_key=acting_key)

    def delete_sftp_ssh_keys_by_id(self, id: str, *, query: Optional[Mapping[str, Any]] = None, idempotency_key: Optional[str] = None, raw: bool = False, organization: Optional[str] = None, acting_key: Optional[str] = None) -> Any:
        """Remove an SSH key: DELETE /v1/sftp/ssh-keys/{id}

        That key can no longer sign in.
        """
        return self.request("DELETE", "/v1/sftp/ssh-keys/" + _segment(id, "id"), query=query, idempotency_key=idempotency_key, raw=raw, organization=organization, acting_key=acting_key)

    def post_jobs(self, body: Dict[str, Any], *, query: Optional[Mapping[str, Any]] = None, idempotency_key: Optional[str] = None, raw: bool = False, organization: Optional[str] = None, acting_key: Optional[str] = None) -> Any:
        """Make a batch job: POST /v1/jobs

        For more than one call can carry: name the route (route, as POST /v1/claims/validate) and,
        if every call carries one, the query. You get the job and a signed upload form for its file:
        { calls: [ ... ] }, each entry exactly the body that route takes in one call, up to 1,000
        calls and 50 MB, uploaded within an hour. Each call is answered as the route answers it and
        held to its limits (2 MB a call; a larger one is answered 413 too_large at its index). A job
        runs claim validation, 837P writing, remittance, eligibility and claim status explanations,
        analytics, payment variance and claim tracking. POST /v1/eligibility/270 as a job is
        different: its file is that route’s own body with up to 7,500 requests, written as one 270.
        A file whose requests would write more segments than one 270 is read in fails the job
        job_270_too_many_segments. A job keeps your file until it finishes and its result for 24
        hours, which no other route does: use invented data with a test key. Off until Payerworks
        turns it on; test keys only for now; 50 jobs a UTC day for an account’s keys together.
        Nothing is sent to a payer.
        """
        return self.request("POST", "/v1/jobs", body=body, query=query, idempotency_key=idempotency_key, raw=raw, organization=organization, acting_key=acting_key)

    def get_jobs(self, *, query: Optional[Mapping[str, Any]] = None, idempotency_key: Optional[str] = None, raw: bool = False, organization: Optional[str] = None, acting_key: Optional[str] = None) -> Any:
        """Your batch jobs: GET /v1/jobs

        This key’s jobs, newest first, at most 100: each one’s route, state and counts. A job whose
        result’s 24 hours are over is listed as expired, with nothing kept, until its record is
        removed.
        """
        return self.request("GET", "/v1/jobs", query=query, idempotency_key=idempotency_key, raw=raw, organization=organization, acting_key=acting_key)

    def get_jobs_by_id(self, id: str, *, query: Optional[Mapping[str, Any]] = None, idempotency_key: Optional[str] = None, raw: bool = False, organization: Optional[str] = None, acting_key: Optional[str] = None) -> Any:
        """One batch job: GET /v1/jobs/{id}

        Its state (awaiting_upload, running, completed, partial, failed, canceled or expired), its
        calls by status, and once it has finished, a link to its result good for 15 minutes; ask
        again for a new one. The result is { answers: [ { index, status, body } ] }, each answer
        what that call would have answered. A job that ran out of time is partial: the answered
        calls are in the result and not_run_from is the first to send again. A job that failed says
        why in error. A job.completed or job.failed event reaches your webhook and destinations when
        it ends. A job whose file arrived but could not be started on our side within an hour fails
        with job_not_started.
        """
        return self.request("GET", "/v1/jobs/" + _segment(id, "id"), query=query, idempotency_key=idempotency_key, raw=raw, organization=organization, acting_key=acting_key)

    def post_jobs_by_id_cancel(self, id: str, body: Optional[Dict[str, Any]] = None, *, query: Optional[Mapping[str, Any]] = None, idempotency_key: Optional[str] = None, raw: bool = False, organization: Optional[str] = None, acting_key: Optional[str] = None) -> Any:
        """Cancel a batch job: POST /v1/jobs/{id}/cancel

        Before its file is uploaded, nothing will run. While it runs, it stops between two calls and
        keeps no result. A finished job cannot be canceled: delete it.
        """
        return self.request("POST", "/v1/jobs/" + _segment(id, "id") + "/cancel", body=body, query=query, idempotency_key=idempotency_key, raw=raw, organization=organization, acting_key=acting_key)

    def delete_jobs_by_id(self, id: str, *, query: Optional[Mapping[str, Any]] = None, idempotency_key: Optional[str] = None, raw: bool = False, organization: Optional[str] = None, acting_key: Optional[str] = None) -> Any:
        """Delete a batch job: DELETE /v1/jobs/{id}

        Its file, its result and the job itself, now rather than after 24 hours. A running job must
        be canceled first.
        """
        return self.request("DELETE", "/v1/jobs/" + _segment(id, "id"), query=query, idempotency_key=idempotency_key, raw=raw, organization=organization, acting_key=acting_key)

    def post_claims_submit(self, body: Dict[str, Any], *, query: Optional[Mapping[str, Any]] = None, idempotency_key: Optional[str] = None, raw: bool = False, organization: Optional[str] = None, acting_key: Optional[str] = None) -> Any:
        """Send claims to a payer: POST /v1/claims/submit

        Not offered yet: Payerworks is connected to no payer, so this answers 501 submit_not_offered
        with each gate and whether it is open (sending switched on, a live key, a signed business
        associate agreement recorded for your account, a trading partner for the payer, and that
        partner switched on). Nothing is written or sent. Once every gate is open it writes your
        claims as an 837P exactly as POST /v1/claims/837p does, under the partner’s interchange ids
        and a control number Payerworks assigns, and hands the file to the partner; transmitted is
        true only when the partner’s connection accepted the file. Every patientControlNumber begins
        with your account’s reference prefix, and one already in flight is refused unless it was
        rejected or the claim is a replacement or a void. The partner’s 999, 277CA and 835 arrive as
        submission events to the key that sent the file, never to anyone else. Send an
        Idempotency-Key: a retry under it answers the first answer and sends nothing again.
        """
        return self.request("POST", "/v1/claims/submit", body=body, query=query, idempotency_key=idempotency_key, raw=raw, organization=organization, acting_key=acting_key)

    def get_partners(self, *, query: Optional[Mapping[str, Any]] = None, idempotency_key: Optional[str] = None, raw: bool = False, organization: Optional[str] = None, acting_key: Optional[str] = None) -> Any:
        """Trading partners: GET /v1/partners

        The trading partners claims go to through POST /v1/claims/submit today, with the payer ids
        each takes and the transactions it carries, and one sentence about Payerworks’ connections.
        Today the list is empty: Payerworks is connected to no payer.
        """
        return self.request("GET", "/v1/partners", query=query, idempotency_key=idempotency_key, raw=raw, organization=organization, acting_key=acting_key)

    def get_audit_log(self, *, query: Optional[Mapping[str, Any]] = None, idempotency_key: Optional[str] = None, raw: bool = False, organization: Optional[str] = None, acting_key: Optional[str] = None) -> Any:
        """Your account’s audit log: GET /v1/audit-log

        Every change to this account, newest first: keys made, rotated, revoked or expired; the
        webhook, destinations and the notice address; providers, enrollment requests, tasks and
        documents; the SFTP user and its SSH keys; batch jobs; the organization and its members.
        Each entry says what, when, who (a key, a member, Payerworks, or Payerworks automatically),
        through which door, the object’s id, and the fields that changed by name only, never a
        value; a web address by its host. limit 1 to 100 (25), type (an entry type, or its family
        before the dot), created_gte and created_lt, starting_after. Each entry is kept 365 days,
        then deleted, and never changed before then. Every key of the account reads the same log.

        query: limit (How many entries a page holds, 1 to 100.); type (An entry type (key.created,
        key.rotated and the rest the docs list), or its family before the dot (key, webhook,
        webhook_destination and the rest).); created_gte (Only those made at or after this instant,
        such as 2026-10-19T14:00:00Z.); created_lt (Only those made before this instant.);
        starting_after (The next_cursor of an earlier page of this audit log.).
        """
        return self.request("GET", "/v1/audit-log", query=query, idempotency_key=idempotency_key, raw=raw, organization=organization, acting_key=acting_key)

    def get_audit_log_export(self, *, query: Optional[Mapping[str, Any]] = None, idempotency_key: Optional[str] = None, raw: bool = False, organization: Optional[str] = None, acting_key: Optional[str] = None) -> Any:
        """Download the audit log: GET /v1/audit-log/export

        The same entries and filters as a CSV file in the answer (csv, filename, columns), at most
        5,000 entries a call; next_cursor names where the rest start. No cell opens as a formula in
        a spreadsheet.

        query: type (An entry type (key.created, key.rotated and the rest the docs list), or its
        family before the dot (key, webhook, webhook_destination and the rest).); created_gte (Only
        those made at or after this instant, such as 2026-10-19T14:00:00Z.); created_lt (Only those
        made before this instant.); starting_after (The next_cursor of an earlier page of this audit
        log.).
        """
        return self.request("GET", "/v1/audit-log/export", query=query, idempotency_key=idempotency_key, raw=raw, organization=organization, acting_key=acting_key)

    def get_org_roles(self, *, query: Optional[Mapping[str, Any]] = None, idempotency_key: Optional[str] = None, raw: bool = False, organization: Optional[str] = None, acting_key: Optional[str] = None) -> Any:
        """The roles: GET /v1/org/roles

        The one role table: every role, every permission and the owner’s rules. Readable with a key
        or a sign-in.
        """
        return self.request("GET", "/v1/org/roles", query=query, idempotency_key=idempotency_key, raw=raw, organization=organization, acting_key=acting_key)

    def get_org_memberships(self, *, query: Optional[Mapping[str, Any]] = None, idempotency_key: Optional[str] = None, raw: bool = False, organization: Optional[str] = None, acting_key: Optional[str] = None) -> Any:
        """Your organizations: GET /v1/org/memberships

        The organizations this sign-in belongs to, with its role in each. A sign-in only.
        """
        return self.request("GET", "/v1/org/memberships", query=query, idempotency_key=idempotency_key, raw=raw, organization=organization, acting_key=acting_key)

    def post_org(self, body: Dict[str, Any], *, query: Optional[Mapping[str, Any]] = None, idempotency_key: Optional[str] = None, raw: bool = False, organization: Optional[str] = None, acting_key: Optional[str] = None) -> Any:
        """Make an organization: POST /v1/org

        Make an organization for an account: name it and give one key of the account (api_key). The
        key proves the account and is checked and not kept; you become its owner. An account belongs
        to one organization. A sign-in only.
        """
        return self.request("POST", "/v1/org", body=body, query=query, idempotency_key=idempotency_key, raw=raw, organization=organization, acting_key=acting_key)

    def get_org(self, *, query: Optional[Mapping[str, Any]] = None, idempotency_key: Optional[str] = None, raw: bool = False, organization: Optional[str] = None, acting_key: Optional[str] = None) -> Any:
        """Your organization: GET /v1/org

        The organization, with your role and what it lets you do. Name it in the
        Payerworks-Organization header when your sign-in belongs to several. A sign-in only.
        """
        return self.request("GET", "/v1/org", query=query, idempotency_key=idempotency_key, raw=raw, organization=organization, acting_key=acting_key)

    def put_org(self, body: Dict[str, Any], *, query: Optional[Mapping[str, Any]] = None, idempotency_key: Optional[str] = None, raw: bool = False, organization: Optional[str] = None, acting_key: Optional[str] = None) -> Any:
        """Change your organization: PUT /v1/org

        Rename it, or choose who manages the account’s keys (key_management): keys_and_members, the
        default, where an API key of the account may still make, rotate and revoke its keys, or
        members_only, where only a signed-in member with the role for it may. An owner’s.
        """
        return self.request("PUT", "/v1/org", body=body, query=query, idempotency_key=idempotency_key, raw=raw, organization=organization, acting_key=acting_key)

    def get_org_members(self, *, query: Optional[Mapping[str, Any]] = None, idempotency_key: Optional[str] = None, raw: bool = False, organization: Optional[str] = None, acting_key: Optional[str] = None) -> Any:
        """The members: GET /v1/org/members

        Every member: the email address of the sign-in, the name, the role, when they joined and who
        invited them. A sign-in only.
        """
        return self.request("GET", "/v1/org/members", query=query, idempotency_key=idempotency_key, raw=raw, organization=organization, acting_key=acting_key)

    def put_org_members_by_id(self, id: str, body: Dict[str, Any], *, query: Optional[Mapping[str, Any]] = None, idempotency_key: Optional[str] = None, raw: bool = False, organization: Optional[str] = None, acting_key: Optional[str] = None) -> Any:
        """Change a member’s role: PUT /v1/org/members/{id}

        A new role: owner, admin, developer or viewer. Only an owner makes, changes or demotes an
        owner, and the last owner is never demoted.
        """
        return self.request("PUT", "/v1/org/members/" + _segment(id, "id"), body=body, query=query, idempotency_key=idempotency_key, raw=raw, organization=organization, acting_key=acting_key)

    def delete_org_members_by_id(self, id: str, *, query: Optional[Mapping[str, Any]] = None, idempotency_key: Optional[str] = None, raw: bool = False, organization: Optional[str] = None, acting_key: Optional[str] = None) -> Any:
        """Remove a member: DELETE /v1/org/members/{id}

        The member loses access at once. revoke_keys=true, an owner’s choice, also revokes the keys
        they made or rotated in the console (each one whose secret they were shown); left out, those
        keys keep working and are listed in the answer. The last owner is never removed, and a
        member may remove themselves to leave.

        query: revoke_keys (An owner’s choice: true also revokes the keys the member made or rotated
        in the console. Left out, they keep working and are listed.).
        """
        return self.request("DELETE", "/v1/org/members/" + _segment(id, "id"), query=query, idempotency_key=idempotency_key, raw=raw, organization=organization, acting_key=acting_key)

    def get_org_invitations(self, *, query: Optional[Mapping[str, Any]] = None, idempotency_key: Optional[str] = None, raw: bool = False, organization: Optional[str] = None, acting_key: Optional[str] = None) -> Any:
        """The invitations waiting: GET /v1/org/invitations

        Every invitation not yet accepted or canceled: the address, the role, who invited and until
        when the link works. A sign-in only.
        """
        return self.request("GET", "/v1/org/invitations", query=query, idempotency_key=idempotency_key, raw=raw, organization=organization, acting_key=acting_key)

    def post_org_invitations(self, body: Dict[str, Any], *, query: Optional[Mapping[str, Any]] = None, idempotency_key: Optional[str] = None, raw: bool = False, organization: Optional[str] = None, acting_key: Optional[str] = None) -> Any:
        """Invite a person: POST /v1/org/invitations

        Invite a person by email with a role. Payerworks emails a link that works for 7 days, and
        only for a sign-in at that verified address; the email names neither the organization nor
        who invited. An organization sends at most 25 invitations a UTC day.
        """
        return self.request("POST", "/v1/org/invitations", body=body, query=query, idempotency_key=idempotency_key, raw=raw, organization=organization, acting_key=acting_key)

    def delete_org_invitations_by_id(self, id: str, *, query: Optional[Mapping[str, Any]] = None, idempotency_key: Optional[str] = None, raw: bool = False, organization: Optional[str] = None, acting_key: Optional[str] = None) -> Any:
        """Cancel an invitation: DELETE /v1/org/invitations/{id}

        The link stops working at once.
        """
        return self.request("DELETE", "/v1/org/invitations/" + _segment(id, "id"), query=query, idempotency_key=idempotency_key, raw=raw, organization=organization, acting_key=acting_key)

    def post_org_join(self, body: Dict[str, Any], *, query: Optional[Mapping[str, Any]] = None, idempotency_key: Optional[str] = None, raw: bool = False, organization: Optional[str] = None, acting_key: Optional[str] = None) -> Any:
        """Accept an invitation: POST /v1/org/join

        Accept an invitation with the token from its link (the part after #join=). The sign-in’s
        verified email address must be the address the invitation was sent to. A sign-in only.
        """
        return self.request("POST", "/v1/org/join", body=body, query=query, idempotency_key=idempotency_key, raw=raw, organization=organization, acting_key=acting_key)

    def get_org_keys(self, *, query: Optional[Mapping[str, Any]] = None, idempotency_key: Optional[str] = None, raw: bool = False, organization: Optional[str] = None, acting_key: Optional[str] = None) -> Any:
        """The account’s keys: GET /v1/org/keys

        Every key of the organization’s account, test and live, as GET /v1/keys lists them, with who
        made each: a member in the console, or a key. Never a key itself. A sign-in only.
        """
        return self.request("GET", "/v1/org/keys", query=query, idempotency_key=idempotency_key, raw=raw, organization=organization, acting_key=acting_key)


# ── Checking a webhook ────────────────────────────────────────────────────────


def _body_bytes(raw_body: Any) -> Optional[bytes]:
    if isinstance(raw_body, str):
        return raw_body.encode("utf-8")
    if isinstance(raw_body, (bytes, bytearray, memoryview)):
        return bytes(raw_body)
    return None


def _fresh(t: int, tolerance: float, now: Optional[float]) -> bool:
    current = time.time() if now is None else float(now)
    return abs(int(current) - t) <= tolerance


def _base64_bytes(text: str) -> Optional[bytes]:
    value = str(text).strip().replace("-", "+").replace("_", "/")
    if not re.fullmatch(r"[A-Za-z0-9+/]*={0,2}", value):
        return None
    value = value.rstrip("=")
    if len(value) % 4 == 1:
        return None
    value += "=" * (-len(value) % 4)
    try:
        return base64.b64decode(value, validate=True)
    except (binascii.Error, ValueError):
        return None


def verify_payerworks_signature(secret: str, header: Optional[str], raw_body: _RawBody, *,
                                tolerance: float = WEBHOOK_TOLERANCE_SECONDS, now: Optional[float] = None) -> bool:
    """Does the Payerworks-Signature header prove raw_body came from Payerworks under secret?

    The header is t=<unix seconds>,v1=<hex HMAC-SHA256 of "<t>.<raw body>" keyed with the
    secret>. Any v1 may match; a time more than tolerance seconds (300) from now (unix seconds)
    is refused. Compared in constant time. Never raises: anything malformed is False.
    """
    body = _body_bytes(raw_body)
    if not secret or not isinstance(header, str) or body is None:
        return False
    t = None
    macs = []
    for part in header.split(","):
        name, sep, value = part.partition("=")
        name = name.strip()
        value = value.strip()
        if not sep or not name:
            continue
        if name == "t" and re.fullmatch(r"[0-9]{1,12}", value):
            t = int(value)
        if name == "v1" and re.fullmatch(r"[0-9a-fA-F]{64}", value):
            macs.append(value.lower())
    if t is None or not macs or not _fresh(t, tolerance, now):
        return False
    expected = hmac.new(str(secret).encode("utf-8"), str(t).encode("ascii") + b"." + body, hashlib.sha256).hexdigest()
    return any(hmac.compare_digest(expected, mac) for mac in macs)


def verify_standard_webhook(secret: str, headers: Mapping[str, Any], raw_body: _RawBody, *,
                            tolerance: float = WEBHOOK_TOLERANCE_SECONDS, now: Optional[float] = None) -> bool:
    """Do Standard Webhooks headers (webhook-id, webhook-timestamp, webhook-signature) prove raw_body?

    The secret is whsec_ and base64; each signature is v1, a comma and the base64 HMAC-SHA256 of
    "<webhook-id>.<webhook-timestamp>.<raw body>" keyed with the decoded secret, several
    separated by spaces. A timestamp more than tolerance seconds (300) from now is refused.
    Compared in constant time. Never raises.
    """
    body = _body_bytes(raw_body)
    if not secret or body is None or headers is None:
        return False
    lowered = {str(k).lower(): v for k, v in headers.items()}
    msg_id = str(lowered.get("webhook-id") or "")
    stamp = str(lowered.get("webhook-timestamp") or "").strip()
    signature = str(lowered.get("webhook-signature") or "")
    if not msg_id or not signature or not re.fullmatch(r"[0-9]{1,12}", stamp):
        return False
    if not _fresh(int(stamp), tolerance, now):
        return False
    key = _base64_bytes(secret[6:] if str(secret).startswith("whsec_") else secret)
    if not key:
        return False
    signed = msg_id.encode("utf-8") + b"." + stamp.encode("ascii") + b"." + body
    expected = hmac.new(key, signed, hashlib.sha256).digest()
    for entry in signature.split(" "):
        version, sep, value = entry.partition(",")
        if version != "v1" or not sep:
            continue
        mac = _base64_bytes(value)
        if mac is not None and hmac.compare_digest(expected, mac):
            return True
    return False
