{"openapi":"3.1.0","info":{"title":"RiskWatch API","version":"1.0.0","description":"API-first monitoring of Polish contractor risk signals from VAT whitelist, KRS and KRZ. Mutating requests require an Idempotency-Key header. Responses use a {data, meta} envelope; errors use {error, meta}. Cursor pagination is available on /watchlist, /events, /entities/{nip}/events and /webhooks/{id}/deliveries; other collection endpoints return their complete bounded result."},"servers":[{"url":"/v1","description":"Relative RiskWatch API v1 base"},{"url":"https://api.riskwatch.pl/v1","description":"Production RiskWatch API v1"}],"security":[{"bearerAuth":[]}],"components":{"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"rw_live or rw_test API key","description":"API keys carry one or more scopes. rw_live keys hit production data. rw_test keys are a sandbox: they never read or write your organization data, are not metered, and only serve deterministic fixtures (marked `sandbox: true`) for the synthetic NIPs 1111111111, 2222222222 and 3333333333; any other NIP returns 404, watchlist/refresh writes are acknowledged without storing or queuing anything, webhook routes and the one-shot checks (/checks/*) return 403, and rw_test keys are rejected by /mcp (403; use a live key or OAuth). `read` — all GET endpoints plus the read-only lookups POST /watchlist/lookup, POST /checks/vat, POST /checks/bank-account, POST /checks/vies and POST /checks/sanctions; `write` — mutating watchlist endpoints (POST/DELETE /watchlist, POST /entities/{nip}/refresh) and also satisfies `webhooks`-scoped routes; `webhooks` — POST /webhooks specifically (a `write`-scoped key also passes this check). A key missing the required scope for a route gets 403 FORBIDDEN. See requiredPublicScope() in src/public-api/app.ts for the authoritative mapping."}},"schemas":{"Envelope":{"type":"object","required":["data","meta"],"properties":{"data":true,"meta":{"type":"object","required":["request_id","timestamp"],"properties":{"request_id":{"type":"string"},"timestamp":{"type":"string","format":"date-time"},"next_cursor":{"type":"string","description":"Present on list endpoints when more rows are available."}}}}},"ErrorEnvelope":{"type":"object","required":["error","meta"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"enum":["VALIDATION_ERROR","UNAUTHORIZED","FORBIDDEN","NOT_FOUND","CONFLICT","RATE_LIMITED","INTERNAL_ERROR","SOURCE_ERROR","SOURCE_UNAVAILABLE"]},"message":{"type":"string"},"retry_after":{"type":"integer","description":"Seconds until the limit resets (RATE_LIMITED / SOURCE_UNAVAILABLE)."},"details":{"type":"object","description":"Structured context, e.g. { issues: [{ path, message }] } for VALIDATION_ERROR."}}},"meta":{"type":"object","required":["request_id","timestamp"],"properties":{"request_id":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}}}}},"WatchlistAddRequest":{"type":"object","required":["nips"],"properties":{"nips":{"type":"array","minItems":1,"maxItems":500,"items":{"type":"string","pattern":"^[0-9]{10}$","description":"Polish NIP (checksum-validated; formatting characters are stripped)"}},"sources":{"type":"array","minItems":1,"items":{"enum":["VAT","KRS","KRZ"]},"default":["VAT"]},"label":{"type":"string","maxLength":160},"external_id":{"type":"string","maxLength":160},"metadata":{"type":"object","additionalProperties":true,"description":"Arbitrary client metadata. The serialized JSON must be 4096 bytes or fewer (enforced server-side; oversized payloads fail with VALIDATION_ERROR). Omit it to keep the metadata a re-activated (previously deleted or paused) row already stores."},"check_frequency":{"enum":["daily","weekly","manual"],"default":"daily"}}},"WatchlistDeleteRequest":{"type":"object","required":["nips"],"properties":{"nips":{"type":"array","minItems":1,"maxItems":500,"items":{"type":"string","pattern":"^[0-9]{10}$"}}}},"CheckVatRequest":{"type":"object","required":["nips"],"additionalProperties":false,"properties":{"nips":{"type":"array","minItems":1,"maxItems":30,"items":{"type":"string","pattern":"^[0-9]{10}$","description":"Polish NIP (checksum-validated before any upstream call; formatting characters are stripped)"}},"date":{"type":"string","format":"date","description":"Register state as of this day (YYYY-MM-DD). Defaults to today (UTC)."}}},"CheckVatResponse":{"type":"object","required":["request_id","date","results"],"properties":{"request_id":{"type":["string","null"],"description":"MF whitelist requestId — keep it as proof of the check."},"date":{"type":"string","format":"date"},"results":{"type":"array","description":"One entry per requested NIP, in request order, including NIPs absent from the whitelist.","items":{"type":"object","required":["nip","found","status_vat","checked_at"],"properties":{"nip":{"type":"string"},"found":{"type":"boolean"},"status_vat":{"enum":["Czynny","Zwolniony","Niezarejestrowany","Wykreślony","Przywrócony","UNKNOWN"]},"name":{"type":["string","null"]},"regon":{"type":["string","null"]},"krs":{"type":["string","null"]},"residence_address":{"type":["string","null"]},"working_address":{"type":["string","null"]},"bank_accounts_count":{"type":["integer","null"]},"has_virtual_accounts":{"type":["boolean","null"]},"checked_at":{"type":"string","format":"date-time"}}}}}},"CheckBankAccountRequest":{"type":"object","required":["nip","account"],"additionalProperties":false,"properties":{"nip":{"type":"string","pattern":"^[0-9]{10}$","description":"Polish NIP (checksum-validated)"},"account":{"type":"string","maxLength":64,"description":"26-digit NRB; spaces and a PL prefix are accepted. Validated with the IBAN mod-97 checksum."},"date":{"type":"string","format":"date","description":"Defaults to today (UTC)."}}},"CheckBankAccountResponse":{"type":"object","required":["nip","account_masked","date","assigned","request_id","source"],"properties":{"nip":{"type":"string"},"account_masked":{"type":"string","description":"Only the last 4 digits are visible."},"date":{"type":"string","format":"date"},"assigned":{"type":"boolean","description":"true when MF reports the account as assigned to this active VAT taxpayer on that date (accountAssigned = TAK)."},"request_id":{"type":["string","null"],"description":"MF whitelist requestId — keep it as proof of the check."},"source":{"enum":["MF_WHITELIST"]}}},"WebhookCreateRequest":{"type":"object","required":["url"],"properties":{"url":{"type":"string","format":"uri","description":"HTTPS endpoint (HTTP allowed only for localhost in dev). Must not resolve to a private/link-local address."},"event_types":{"type":"array","minItems":1,"default":["*"],"items":{"enum":["*","risk_event.info","risk_event.warning","risk_event.critical"]}},"sources":{"type":"array","minItems":1,"description":"Only deliver events from these sources. Omit for all sources (stored as null). AND-ed with event_types and event_codes.","items":{"enum":["VAT","KRS","KRZ"]}},"event_codes":{"type":"array","minItems":1,"description":"Only deliver these domain event codes (the event_type field of an event). Omit for all. Unknown codes are a 400.","items":{"enum":["VAT_REMOVED","VAT_NOT_REGISTERED","VAT_RESTORED","VAT_STATUS_CHANGED","VAT_BANK_ACCOUNTS_CHANGED","KRS_DELETED","KRS_BANKRUPTCY","KRS_LIQUIDATION","KRS_RESTRUCTURING","KRS_BOARD_CHANGED","KRS_ADDRESS_CHANGED","KRZ_BANKRUPTCY_FOUND","KRZ_RESTRUCTURING_FOUND","KRZ_BUSINESS_BAN_FOUND","KRZ_NEW_PROCEEDING","KRZ_ENFORCEMENT_FOUND","KRZ_PROCEEDING_CLOSED","KRS_REACTIVATED"]}},"payload_format":{"enum":["riskwatch","cloudevents"],"default":"riskwatch","description":"riskwatch: the {id, type, created_at, data} body. cloudevents: a CloudEvents 1.0 structured-mode JSON event sent as application/cloudevents+json (see WebhookCloudEvent)."}}},"Entity":{"type":"object","description":"Row from `entities`, the shared per-NIP record all sources write to. Selected in full (select('*')), so every column is always present; nullability below mirrors the NOT NULL constraints in supabase/migrations/202606280001_core_schema.sql.","required":["nip","krs","regon","name","normalized_name","entity_type","vat_status","krs_status","krz_status","last_vat_check","last_krs_check","last_krz_check","created_at","updated_at"],"properties":{"nip":{"type":"string"},"krs":{"type":["string","null"]},"regon":{"type":["string","null"]},"name":{"type":["string","null"]},"normalized_name":{"type":["string","null"]},"entity_type":{"type":"string"},"vat_status":{"type":["string","null"]},"krs_status":{"type":["string","null"],"description":"DEFAULT 'UNKNOWN' but no NOT NULL constraint — nullable."},"krz_status":{"type":["string","null"],"description":"DEFAULT 'NONE' but no NOT NULL constraint — nullable."},"last_vat_check":{"type":["string","null"],"format":"date-time"},"last_krs_check":{"type":["string","null"],"format":"date-time"},"last_krz_check":{"type":["string","null"],"format":"date-time"},"created_at":{"type":"string","format":"date-time"},"updated_at":{"type":"string","format":"date-time"}}},"RiskScore":{"type":"object","description":"Row from `risk_scores`, select('*'). GET /entities/{nip}/risk returns this shape, or exactly `{}` when no score has been computed yet for the NIP — see RiskScoreResult.","required":["nip","score","band","summary","reasons","computed_at"],"properties":{"nip":{"type":"string"},"score":{"type":"integer","minimum":0,"maximum":100},"band":{"enum":["LOW","MEDIUM","HIGH","CRITICAL"]},"summary":{"type":["string","null"],"description":"Deterministic cited sentence list built only from `reasons`: one English sentence per scored reason citing its event type, source and detected_at, e.g. \"VAT_REMOVED (source VAT, detected 2026-10-02T09:30:00.000Z) adds 50 points.\" \"No scored risk events.\" when nothing is scored. Rows computed before 2026-10-28 may still carry the older one-line template. No language model is involved."},"reasons":{"type":"array","items":{"type":"object"}},"computed_at":{"type":"string","format":"date-time"}}},"RiskScoreResult":{"description":"Result of GET /entities/{nip}/risk: either the computed RiskScore, or exactly `{}` (no properties) when none exists yet.","oneOf":[{"$ref":"#/components/schemas/RiskScore"},{"type":"object","additionalProperties":false}]},"WatchlistItem":{"type":"object","description":"Row from `watchlist` (select('*,entities(*)')) with `risk_scores` attached separately. `watchlist.nip` is a NOT NULL FK to `entities(nip)` so `entities` is always resolved; `watchlist` and `risk_scores` are siblings with no FK between them, so PostgREST cannot embed `risk_scores(*)` directly — attachRiskScores() (src/services/watchlistService.ts) fetches it by nip and attaches it under this key, `null` when no score has been computed yet.","required":["id","organization_id","nip","label","external_id","metadata","sources","check_frequency","status","next_check_at","last_check_at","last_event_at","created_at","entities","risk_scores"],"properties":{"id":{"type":"string","format":"uuid"},"organization_id":{"type":"string","format":"uuid"},"nip":{"type":"string"},"label":{"type":["string","null"]},"external_id":{"type":["string","null"]},"metadata":{"type":["object","null"],"additionalProperties":true,"description":"JSONB column has no NOT NULL constraint; a row created by the write path stores {} when the request omitted it, and a re-activated row keeps whatever it already stored."},"sources":{"type":"array","items":{"enum":["VAT","KRS","KRZ"]}},"check_frequency":{"enum":["daily","weekly","manual"]},"status":{"enum":["active","paused","deleted"]},"next_check_at":{"type":["string","null"],"format":"date-time"},"last_check_at":{"type":["string","null"],"format":"date-time"},"last_event_at":{"type":["string","null"],"format":"date-time"},"created_at":{"type":"string","format":"date-time"},"entities":{"$ref":"#/components/schemas/Entity"},"risk_scores":{"anyOf":[{"$ref":"#/components/schemas/RiskScore"},{"type":"null"}]}}},"EntityStatus":{"type":"object","description":"Row from `watchlist` projected via select('nip,sources,status,label,external_id,metadata,entities(*)'), with `risk_scores` attached separately the same way as WatchlistItem (see attachRiskScores in src/services/watchlistService.ts) — a narrower field set than the full WatchlistItem (no id/organization_id/check_frequency/timestamps).","required":["nip","sources","status","label","external_id","metadata","entities","risk_scores"],"properties":{"nip":{"type":"string"},"sources":{"type":"array","items":{"enum":["VAT","KRS","KRZ"]}},"status":{"enum":["active","paused","deleted"]},"label":{"type":["string","null"]},"external_id":{"type":["string","null"]},"metadata":{"type":["object","null"],"additionalProperties":true},"entities":{"$ref":"#/components/schemas/Entity"},"risk_scores":{"anyOf":[{"$ref":"#/components/schemas/RiskScore"},{"type":"null"}]}}},"Event":{"type":"object","description":"Row from `events` (select('*,entities(name)')). The org-wide and per-entity feeds both go through listWatchlist/listEvents, which always embed `entities(name)`; `events.nip` is a NOT NULL FK to `entities(nip)` so the embed is always resolved.","required":["id","nip","source","event_type","severity","title","description","old_value","new_value","score_impact","payload","fingerprint","verified","detected_at","last_observed_at","resolved_at","entities"],"properties":{"id":{"type":"string","format":"uuid"},"nip":{"type":"string"},"source":{"enum":["VAT","KRS","KRZ"]},"event_type":{"type":"string"},"severity":{"enum":["info","warning","critical"]},"title":{"type":"string"},"description":{"type":["string","null"]},"old_value":{"type":["string","null"]},"new_value":{"type":["string","null"]},"score_impact":{"type":"integer"},"payload":{"type":["object","null"],"additionalProperties":true,"description":"JSONB column has no NOT NULL constraint; nullable."},"fingerprint":{"type":"string"},"verified":{"type":"boolean"},"detected_at":{"type":"string","format":"date-time"},"last_observed_at":{"type":["string","null"],"format":"date-time"},"resolved_at":{"type":["string","null"],"format":"date-time"},"entities":{"type":"object","description":"Always present (NOT NULL FK); `name` itself is a nullable column on `entities`.","required":["name"],"properties":{"name":{"type":["string","null"]}}}}},"Webhook":{"type":"object","description":"Row from `webhooks` as returned by list/create, excluding the plaintext secret (only ever shown once, on create). All selected columns are NOT NULL.","required":["id","url","event_types","secret_prefix","is_active","created_at"],"properties":{"id":{"type":"string","format":"uuid"},"url":{"type":"string","format":"uri"},"event_types":{"type":"array","description":"Subscribed event types. New values may be introduced within /v1 (see the compatibility policy in docs/API.md), so treat this as an open set.","items":{"type":"string","examples":["*","risk_event.info","risk_event.warning","risk_event.critical"]}},"secret_prefix":{"type":"string","description":"Non-secret prefix of the signing secret, for identifying which secret is configured."},"is_active":{"type":"boolean"},"created_at":{"type":"string","format":"date-time"},"sources":{"type":["array","null"],"items":{"type":"string","examples":["VAT","KRS","KRZ"]},"description":"Source filter; null = all sources."},"event_codes":{"type":["array","null"],"items":{"type":"string"},"description":"Event-code filter; null = all codes. Open set on the response side."},"payload_format":{"enum":["riskwatch","cloudevents"]},"previous_secret_prefix":{"type":["string","null"],"description":"Prefix of the previous secret while it still signs after a rotation; null otherwise."},"previous_secret_expires_at":{"type":["string","null"],"format":"date-time","description":"When the previous secret stops signing."}}},"WebhookCreated":{"allOf":[{"$ref":"#/components/schemas/Webhook"},{"type":"object","required":["secret"],"properties":{"secret":{"type":"string","description":"Plaintext signing secret, returned ONLY on the original create. A retry after a lost original response returns 409 CONFLICT with the sanitized original response; list and revoke/recreate the webhook if its secret was lost."}}}]},"WebhookReplayResult":{"type":"object","required":["replayed","status","next_attempt_at"],"properties":{"replayed":{"type":"string","format":"uuid","description":"Delivery id, unchanged — receivers dedupe on it."},"status":{"enum":["pending"]},"next_attempt_at":{"type":["string","null"],"format":"date-time"}}},"WebhookReplayFailedRequest":{"type":"object","required":["since"],"properties":{"since":{"type":"string","format":"date-time","description":"Inclusive lower bound on delivery created_at."},"until":{"type":"string","format":"date-time","description":"Exclusive upper bound; defaults to now. The since..until window must not exceed 7 days."}}},"WebhookReplayFailedResult":{"type":"object","required":["requeued","capped"],"properties":{"requeued":{"type":"integer","minimum":0,"maximum":500},"capped":{"type":"boolean","description":"True when more than 500 failed deliveries matched; call again to requeue the rest."}}},"WebhookDelivery":{"type":"object","description":"Row from `webhook_deliveries` (select filters by webhooks!inner(organization_id), which is why `webhooks` is embedded — the inner join guarantees it is always present).","required":["id","event_id","occurrence_id","status","attempts","max_attempts","next_attempt_at","last_status_code","last_error","created_at","webhooks"],"properties":{"id":{"type":"string","format":"uuid"},"event_id":{"type":"string","format":"uuid"},"occurrence_id":{"type":"string","format":"uuid"},"status":{"enum":["pending","sending","delivered","failed"]},"attempts":{"type":"integer"},"max_attempts":{"type":"integer"},"next_attempt_at":{"type":["string","null"],"format":"date-time"},"last_status_code":{"type":["integer","null"]},"last_error":{"type":["string","null"]},"created_at":{"type":"string","format":"date-time"},"webhooks":{"type":"object","required":["organization_id"],"properties":{"organization_id":{"type":"string","format":"uuid"}}}}},"UsageCounter":{"type":"object","description":"Row from `usage_counters` (one row per metric per billing/daily period, most recent 62 days). All columns are NOT NULL.","required":["organization_id","period_start","metric","used","limit_value"],"properties":{"organization_id":{"type":"string","format":"uuid"},"period_start":{"type":"string","format":"date"},"metric":{"enum":["api_requests","checks","manual_refreshes","watchlist_items","webhooks","krz_checks","batch_imports"]},"used":{"type":"integer"},"limit_value":{"type":"integer"}}},"WatchlistAddResponse":{"type":"object","required":["added","skipped","reactivated","failed","plan_limit"],"properties":{"added":{"type":"array","items":{"type":"string"},"description":"NIPs newly inserted onto the watchlist."},"skipped":{"type":"array","items":{"type":"string"},"description":"NIPs not written: either already active on the watchlist (nothing to do) or refused because the watchlist plan limit was reached."},"reactivated":{"type":"array","items":{"type":"string"},"description":"Paused or deleted NIPs returned to active."},"failed":{"type":"array","items":{"type":"object","required":["nip","error"],"properties":{"nip":{"type":"string"},"error":{"type":"string"}}},"description":"NIPs that were not fully queued, with the per-NIP error."},"plan_limit":{"type":"integer","description":"The organization plan watchlist_limit this add was checked against."}}},"WatchlistDeleteResponse":{"type":"object","required":["removed"],"properties":{"removed":{"type":"array","items":{"type":"string"}}}},"RefreshResponse":{"type":"object","required":["nip","queued_sources","skipped_by_plan"],"properties":{"nip":{"type":"string"},"queued_sources":{"type":"array","items":{"enum":["VAT","KRS","KRZ"]},"description":"Stored sources the plan allows and that were queued."},"skipped_by_plan":{"type":"array","items":{"type":"string"},"description":"Stored sources the current plan does not allow, so they were not queued."}}},"HealthStatus":{"type":"object","required":["ok","service","sources"],"properties":{"ok":{"type":"boolean"},"service":{"type":"string"},"sources":{"type":"object","required":["VAT","KRS","KRZ"],"properties":{"VAT":{"type":"string"},"KRS":{"type":"string"},"KRZ":{"enum":["enabled","disabled"]}}}}},"RiskScoreHistoryEntry":{"type":"object","description":"One distinct computed risk state. A row is written only when score, band or reasons changed; history starts when the 2026-10-28 migration was applied.","required":["id","nip","score","band","reasons","summary","computed_at"],"properties":{"id":{"type":"string","format":"uuid"},"nip":{"type":"string"},"score":{"type":"integer","minimum":0,"maximum":100},"band":{"enum":["LOW","MEDIUM","HIGH","CRITICAL"]},"reasons":{"type":"array","items":{"type":"object"},"description":"Top 10 reasons by absolute impact: event_id, event_type, source, detected_at, impact, severity, title."},"summary":{"type":["string","null"]},"computed_at":{"type":"string","format":"date-time"}}},"RiskChangeExplanation":{"type":"object","description":"The latest history snapshot at or before `from` and at or before `to`, and a deterministic diff of their reasons. Reasons are the top 10 by impact, so this explains the score, not every event; see listEntityEvents for the rest.","required":["nip","from","to","before","after","diff"],"properties":{"nip":{"type":"string"},"from":{"type":"string","format":"date-time"},"to":{"type":"string","format":"date-time"},"before":{"anyOf":[{"$ref":"#/components/schemas/RiskScoreHistoryEntry"},{"type":"null"}]},"after":{"anyOf":[{"$ref":"#/components/schemas/RiskScoreHistoryEntry"},{"type":"null"}]},"diff":{"type":"object","required":["score_before","score_after","score_delta","band_before","band_after","added","removed","changed"],"properties":{"score_before":{"type":["integer","null"]},"score_after":{"type":["integer","null"]},"score_delta":{"type":"integer"},"band_before":{"type":["string","null"]},"band_after":{"type":["string","null"]},"added":{"type":"array","items":{"type":"object"},"description":"Reasons present only in `after`, each with `event` (the causing event row, or null when it is no longer stored)."},"removed":{"type":"array","items":{"type":"object"},"description":"Reasons present only in `before`, each with `event`."},"changed":{"type":"array","items":{"type":"object","required":["event_id","event_type","impact_before","impact_after","impact_delta"],"properties":{"event_id":{"type":["string","null"]},"event_type":{"type":"string"},"source":{"type":["string","null"]},"impact_before":{"type":"integer"},"impact_after":{"type":"integer"},"impact_delta":{"type":"integer"},"event":{"type":["object","null"]}}}}}}}},"PortfolioSummary":{"type":"object","description":"Aggregate over the organization's ACTIVE watchlist. UNSCORED counts NIPs with no computed risk score yet.","required":["total","by_band","by_band_source","open_events_by_source","score_increases_7d","generated_at"],"properties":{"total":{"type":"integer"},"by_band":{"$ref":"#/components/schemas/BandCounts"},"by_band_source":{"type":"object","description":"Band counts per monitored source (a NIP monitored on VAT and KRS counts under both).","properties":{"VAT":{"$ref":"#/components/schemas/BandCounts"},"KRS":{"$ref":"#/components/schemas/BandCounts"},"KRZ":{"$ref":"#/components/schemas/BandCounts"}}},"open_events_by_source":{"type":"object","description":"Unresolved events by source and severity.","additionalProperties":{"type":"object","properties":{"info":{"type":"integer"},"warning":{"type":"integer"},"critical":{"type":"integer"}}}},"score_increases_7d":{"type":"object","required":["available","items"],"description":"NIPs whose score is higher now than 7 days ago, from score history (largest increase first, max 50). `available: false` until score history is enabled.","properties":{"available":{"type":"boolean"},"items":{"type":"array","items":{"type":"object","required":["nip","score_before","score_after","band_after","delta","computed_at"],"properties":{"nip":{"type":"string"},"score_before":{"type":"integer"},"score_after":{"type":"integer"},"band_before":{"type":["string","null"]},"band_after":{"type":"string"},"delta":{"type":"integer"},"computed_at":{"type":"string","format":"date-time"}}}}}},"generated_at":{"type":"string","format":"date-time"}}},"BandCounts":{"type":"object","required":["LOW","MEDIUM","HIGH","CRITICAL","UNSCORED"],"properties":{"LOW":{"type":"integer"},"MEDIUM":{"type":"integer"},"HIGH":{"type":"integer"},"CRITICAL":{"type":"integer"},"UNSCORED":{"type":"integer"}}},"ReviewIntervalRequest":{"type":"object","required":["review_interval_days"],"additionalProperties":false,"properties":{"review_interval_days":{"type":["integer","null"],"minimum":1,"maximum":3650,"description":"Days between reviews of this counterparty (your risk-based policy). null removes it from the review calendar."}}},"ReviewAcknowledgeRequest":{"type":"object","additionalProperties":false,"properties":{"note":{"type":"string","maxLength":500,"description":"Optional reviewer note, stored only in the audit log."}}},"ReviewState":{"type":"object","required":["nip","review_interval_days","last_reviewed_at","next_review_at"],"properties":{"nip":{"type":"string"},"review_interval_days":{"type":["integer","null"]},"last_reviewed_at":{"type":["string","null"],"format":"date-time"},"next_review_at":{"type":["string","null"],"format":"date-time","description":"last_reviewed_at + interval; null until the first review or when no interval is set."},"sandbox":{"type":"boolean","description":"Present (true) on rw_test fixture responses."}}},"ReviewDueItem":{"type":"object","required":["nip","review_interval_days","last_reviewed_at","due_at","overdue_days"],"properties":{"nip":{"type":"string"},"label":{"type":["string","null"]},"entity_name":{"type":["string","null"]},"review_interval_days":{"type":"integer"},"last_reviewed_at":{"type":["string","null"],"format":"date-time","description":"null = never reviewed; the interval then counts from when the NIP was added."},"due_at":{"type":"string","format":"date-time"},"overdue_days":{"type":"integer","minimum":0},"sandbox":{"type":"boolean"}}},"WebhookRotateSecretRequest":{"type":"object","additionalProperties":false,"properties":{"grace_hours":{"type":"integer","minimum":0,"maximum":168,"default":24,"description":"Hours the previous secret keeps signing deliveries alongside the new one. 0 retires it immediately."}}},"WebhookSecretRotated":{"type":"object","required":["id","secret","secret_prefix","previous_secret_prefix","previous_secret_expires_at"],"properties":{"id":{"type":"string","format":"uuid"},"secret":{"type":"string","description":"New plaintext signing secret, returned ONLY by this response. A retry with the same Idempotency-Key returns 409 instead of the secret."},"secret_prefix":{"type":"string"},"previous_secret_prefix":{"type":["string","null"]},"previous_secret_expires_at":{"type":["string","null"],"format":"date-time"}}},"WebhookCloudEvent":{"type":"object","description":"Delivery body when payload_format is cloudevents (CloudEvents 1.0.2, structured content mode, JSON format). The X-RiskWatch-* headers and signature are the same as for the riskwatch format; the signature covers these exact bytes.","required":["specversion","id","source","type","subject","time","datacontenttype","data"],"properties":{"specversion":{"const":"1.0"},"id":{"type":"string","format":"uuid","description":"Delivery id (same as X-RiskWatch-Delivery-ID); retries and replays reuse it."},"source":{"const":"https://api.riskwatch.pl"},"type":{"type":"string","examples":["pl.riskwatch.risk_event.critical"],"description":"pl.riskwatch. + the riskwatch event type. Filters keep using the internal type."},"subject":{"type":"string","description":"NIP of the company."},"time":{"type":"string","format":"date-time","description":"When this occurrence was observed (data.observed_at)."},"datacontenttype":{"const":"application/json"},"data":{"type":"object","description":"Same object as data in the riskwatch format."}}},"ImportRowError":{"type":"object","required":["row","nip","reason"],"properties":{"row":{"type":"integer","description":"CSV line number (header is line 1) or 1-based JSON item index."},"nip":{"type":["string","null"],"description":"The normalized NIP when the row had a valid one; no other input is ever echoed."},"reason":{"enum":["invalid_nip","duplicate_nip","invalid_label","invalid_external_id","usage_limit","plan_limit","forbidden","failed"]}}},"ImportJob":{"type":"object","required":["id","status","total_rows","valid_rows","processed_rows"],"properties":{"job_id":{"type":"string","format":"uuid","description":"Same as id; present on the 202 response."},"id":{"type":"string","format":"uuid"},"status":{"enum":["queued","running","completed","failed"]},"total_rows":{"type":"integer"},"valid_rows":{"type":"integer"},"processed_rows":{"type":"integer","description":"Valid rows already passed to the watchlist; the job resumes from here after a restart."},"added":{"type":"integer"},"reactivated":{"type":"integer"},"skipped":{"type":"integer","description":"Already active on the watchlist (left unchanged)."},"failed":{"type":"integer"},"error_count":{"type":"integer","description":"All row errors, including those beyond the first 1000 listed in errors."},"errors":{"type":"array","maxItems":1000,"items":{"$ref":"#/components/schemas/ImportRowError"}},"sources":{"type":"array","items":{"enum":["VAT","KRS","KRZ"]}},"check_frequency":{"enum":["daily","weekly","manual"]},"last_error":{"enum":[null,"plan_limit","forbidden","transient","organization_inactive"]},"created_at":{"type":"string","format":"date-time"},"updated_at":{"type":"string","format":"date-time"},"completed_at":{"type":["string","null"],"format":"date-time"}}},"ImportJsonRequest":{"type":"object","required":["items"],"additionalProperties":false,"properties":{"items":{"type":"array","minItems":1,"maxItems":25000,"items":{"type":"object","required":["nip"],"additionalProperties":false,"properties":{"nip":{"type":"string"},"label":{"type":"string","maxLength":160},"external_id":{"type":"string","maxLength":160}}}},"sources":{"type":"array","minItems":1,"items":{"enum":["VAT","KRS","KRZ"]},"default":["VAT"]},"check_frequency":{"enum":["daily","weekly","manual"],"default":"daily"}}},"WatchlistLookupRequest":{"type":"object","required":["external_ids"],"additionalProperties":false,"properties":{"external_ids":{"type":"array","minItems":1,"maxItems":500,"items":{"type":"string","minLength":1,"maxLength":160}}}},"WatchlistLookupResult":{"type":"object","required":["results","not_found"],"properties":{"results":{"type":"object","additionalProperties":{"type":"array","items":{"$ref":"#/components/schemas/WatchlistItem"}},"description":"external_id -> matching watchlist rows (several rows may share one external_id)."},"not_found":{"type":"array","items":{"type":"string"}}}},"CheckViesRequest":{"type":"object","required":["country_code","vat_number"],"additionalProperties":false,"properties":{"country_code":{"type":"string","minLength":2,"maxLength":2,"description":"EU member state (VIES code; GR is accepted as EL) or XI."},"vat_number":{"type":"string","maxLength":32,"description":"VAT number with or without the country prefix; spaces, dots and dashes are stripped."},"requester_country_code":{"type":"string","minLength":2,"maxLength":2,"description":"Your own member state. Give together with requester_vat_number to get a VIES consultation number."},"requester_vat_number":{"type":"string","maxLength":32,"description":"Your own EU VAT number."}}},"CheckViesResponse":{"type":"object","required":["country_code","vat_number","valid","name","address","request_date","request_identifier","source"],"properties":{"country_code":{"type":"string"},"vat_number":{"type":"string"},"valid":{"type":"boolean","description":"VIES verdict for intra-EU trade on the request date."},"name":{"type":["string","null"],"description":"As returned by the member state; null when it discloses none (\"---\")."},"address":{"type":["string","null"],"description":"As returned by the member state; null when it discloses none."},"request_date":{"type":["string","null"],"format":"date-time"},"request_identifier":{"type":["string","null"],"description":"VIES consultation number; only issued when requester data was supplied."},"source":{"enum":["VIES"]}}},"CheckSanctionsRequest":{"type":"object","additionalProperties":false,"minProperties":1,"properties":{"nip":{"type":"string","pattern":"^[0-9]{10}$","description":"Polish NIP (checksum-validated; formatting characters are stripped)."},"name":{"type":"string","minLength":2,"maxLength":300,"description":"Legal or personal name, matched exactly after normalization (case, diacritics, punctuation, spacing). No fuzzy matching."}}},"CheckSanctionsResponse":{"type":"object","required":["query","result","matches","lists","stale","message","disclaimer"],"properties":{"query":{"type":"object","properties":{"nip":{"type":["string","null"]},"name":{"type":["string","null"]},"normalized_name":{"type":["string","null"]}}},"result":{"enum":["match","no_match","inconclusive"],"description":"inconclusive: no match found but at least one list is stale (older than 48h) or never synced; never read it as not sanctioned."},"matches":{"type":"array","items":{"type":"object","required":["entry_id","list_code","external_id","name","matched_on","listed_at","source_url","synced_at"],"properties":{"entry_id":{"type":"string","format":"uuid"},"list_code":{"enum":["PL_MSWIA","EU_FSF","US_OFAC_SDN"]},"external_id":{"type":"string","description":"EU reference number, OFAC ent_num, or a stable hash for MSWiA rows."},"name":{"type":"string"},"matched_on":{"enum":["nip","name","alias"]},"listed_at":{"type":["string","null"],"format":"date"},"source_url":{"type":"string"},"synced_at":{"type":"string","format":"date-time"}}}},"lists":{"type":"array","items":{"type":"object","required":["list_code","synced_at","stale"],"properties":{"list_code":{"enum":["PL_MSWIA","EU_FSF","US_OFAC_SDN"]},"synced_at":{"type":["string","null"],"format":"date-time"},"stale":{"type":"boolean"},"entry_count":{"type":["integer","null"]},"source_url":{"type":["string","null"]}}}},"stale":{"type":"boolean"},"message":{"type":"string"},"disclaimer":{"type":"string"}}}}},"paths":{"/health":{"get":{"operationId":"getHealth","security":[],"summary":"Health status","description":"Returns service health and availability configuration for VAT, KRS and KRZ registries.","responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/Envelope"},{"type":"object","properties":{"data":{"$ref":"#/components/schemas/HealthStatus"}}}]}}}}}}},"/watchlist":{"get":{"operationId":"listWatchlist","summary":"List watchlist","description":"Lists monitored Polish NIPs on the organization watchlist with current entity details and risk scores.","parameters":[{"name":"limit","in":"query","required":false,"schema":{"type":"integer","minimum":1,"maximum":500,"default":100}},{"name":"cursor","in":"query","required":false,"schema":{"type":"string"},"description":"Opaque keyset cursor from a previous response meta.next_cursor."},{"name":"band","in":"query","required":false,"style":"form","explode":false,"description":"Comma-separated risk bands; only entities with a computed score match.","schema":{"type":"array","items":{"enum":["LOW","MEDIUM","HIGH","CRITICAL"]}}},{"name":"status","in":"query","required":false,"schema":{"enum":["active","paused"]}},{"name":"source","in":"query","required":false,"schema":{"enum":["VAT","KRS","KRZ"]},"description":"Rows monitoring this source."},{"name":"label_prefix","in":"query","required":false,"schema":{"type":"string","minLength":1,"maxLength":160},"description":"Case-insensitive label prefix; % and _ match literally, * is rejected."},{"name":"external_id","in":"query","required":false,"schema":{"type":"string","minLength":1,"maxLength":160},"description":"Exact external_id match (not unique: several rows may share one)."}],"responses":{"200":{"description":"Watchlist page","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/Envelope"},{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/WatchlistItem"}}}}]}}}},"400":{"description":"VALIDATION_ERROR — malformed body/params (includes error.details.issues)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"401":{"description":"UNAUTHORIZED — missing, malformed, revoked or expired API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"FORBIDDEN — API key lacks the required scope, plan limit, or source not on plan","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"404":{"description":"NOT_FOUND — entity is not on this organization watchlist","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"409":{"description":"CONFLICT — Idempotency-Key reused, still processing, outcome indeterminate, or one-time secret cannot be replayed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"description":"RATE_LIMITED — usage/plan limit exceeded (see error.retry_after)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"500":{"description":"INTERNAL_ERROR","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"502":{"description":"SOURCE_ERROR — upstream official source returned an error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"503":{"description":"SOURCE_UNAVAILABLE — upstream official source rate limited or unavailable","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}},"post":{"operationId":"addWatchlist","summary":"Add NIPs to watchlist","description":"Adds or reactivates up to 500 Polish NIPs on the organization watchlist and queues initial registry checks.","parameters":[{"name":"Idempotency-Key","in":"header","required":true,"description":"Required for POST/DELETE requests; PATCH does not currently require this header. Printable ASCII, 1-128 characters, not starting with the reserved prefix \"user:\". Reuse only for the exact same method, path and body.","schema":{"type":"string","minLength":1,"maxLength":128,"pattern":"^[ -~]+$"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WatchlistAddRequest"}}}},"responses":{"200":{"description":"Added","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/Envelope"},{"type":"object","properties":{"data":{"$ref":"#/components/schemas/WatchlistAddResponse"}}}]}}}},"400":{"description":"VALIDATION_ERROR — malformed body/params (includes error.details.issues)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"401":{"description":"UNAUTHORIZED — missing, malformed, revoked or expired API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"FORBIDDEN — API key lacks the required scope, plan limit, or source not on plan","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"404":{"description":"NOT_FOUND — entity is not on this organization watchlist","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"409":{"description":"CONFLICT — Idempotency-Key reused, still processing, outcome indeterminate, or one-time secret cannot be replayed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"description":"RATE_LIMITED — usage/plan limit exceeded (see error.retry_after)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"500":{"description":"INTERNAL_ERROR","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"502":{"description":"SOURCE_ERROR — upstream official source returned an error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"503":{"description":"SOURCE_UNAVAILABLE — upstream official source rate limited or unavailable","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}},"delete":{"operationId":"removeWatchlist","summary":"Remove NIPs from watchlist","description":"Marks one or more monitored NIPs as deleted on the organization watchlist.","parameters":[{"name":"Idempotency-Key","in":"header","required":true,"description":"Required for POST/DELETE requests; PATCH does not currently require this header. Printable ASCII, 1-128 characters, not starting with the reserved prefix \"user:\". Reuse only for the exact same method, path and body.","schema":{"type":"string","minLength":1,"maxLength":128,"pattern":"^[ -~]+$"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WatchlistDeleteRequest"}}}},"responses":{"200":{"description":"Removed","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/Envelope"},{"type":"object","properties":{"data":{"$ref":"#/components/schemas/WatchlistDeleteResponse"}}}]}}}},"400":{"description":"VALIDATION_ERROR — malformed body/params (includes error.details.issues)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"401":{"description":"UNAUTHORIZED — missing, malformed, revoked or expired API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"FORBIDDEN — API key lacks the required scope, plan limit, or source not on plan","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"404":{"description":"NOT_FOUND — entity is not on this organization watchlist","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"409":{"description":"CONFLICT — Idempotency-Key reused, still processing, outcome indeterminate, or one-time secret cannot be replayed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"description":"RATE_LIMITED — usage/plan limit exceeded (see error.retry_after)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"500":{"description":"INTERNAL_ERROR","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"502":{"description":"SOURCE_ERROR — upstream official source returned an error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"503":{"description":"SOURCE_UNAVAILABLE — upstream official source rate limited or unavailable","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}}},"/entities/{nip}":{"get":{"operationId":"getEntityStatus","summary":"Entity status","description":"Returns current VAT, KRS and KRZ registry status, metadata and latest risk score for a monitored 10-digit Polish NIP.","parameters":[{"name":"nip","in":"path","required":true,"description":"Polish NIP, 10 digits, checksum-validated (formatting characters are stripped at runtime)","schema":{"type":"string","pattern":"^[0-9]{10}$","description":"Polish NIP, 10 digits, checksum-validated"}}],"responses":{"200":{"description":"Entity","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/Envelope"},{"type":"object","properties":{"data":{"$ref":"#/components/schemas/EntityStatus"}}}]}}}},"400":{"description":"VALIDATION_ERROR — malformed body/params (includes error.details.issues)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"401":{"description":"UNAUTHORIZED — missing, malformed, revoked or expired API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"FORBIDDEN — API key lacks the required scope, plan limit, or source not on plan","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"404":{"description":"NOT_FOUND — entity is not on this organization watchlist","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"409":{"description":"CONFLICT — Idempotency-Key reused, still processing, outcome indeterminate, or one-time secret cannot be replayed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"description":"RATE_LIMITED — usage/plan limit exceeded (see error.retry_after)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"500":{"description":"INTERNAL_ERROR","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"502":{"description":"SOURCE_ERROR — upstream official source returned an error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"503":{"description":"SOURCE_UNAVAILABLE — upstream official source rate limited or unavailable","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}}},"/entities/{nip}/risk":{"get":{"operationId":"getEntityRisk","summary":"Entity risk","description":"Returns the computed risk score (0-100), risk band (LOW, MEDIUM, HIGH, CRITICAL) and weighted reasons for a monitored Polish NIP.","parameters":[{"name":"nip","in":"path","required":true,"description":"Polish NIP, 10 digits, checksum-validated (formatting characters are stripped at runtime)","schema":{"type":"string","pattern":"^[0-9]{10}$","description":"Polish NIP, 10 digits, checksum-validated"}}],"responses":{"200":{"description":"Risk score, or {} if none has been computed yet","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/Envelope"},{"type":"object","properties":{"data":{"$ref":"#/components/schemas/RiskScoreResult"}}}]}}}},"400":{"description":"VALIDATION_ERROR — malformed body/params (includes error.details.issues)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"401":{"description":"UNAUTHORIZED — missing, malformed, revoked or expired API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"FORBIDDEN — API key lacks the required scope, plan limit, or source not on plan","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"404":{"description":"NOT_FOUND — entity is not on this organization watchlist","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"409":{"description":"CONFLICT — Idempotency-Key reused, still processing, outcome indeterminate, or one-time secret cannot be replayed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"description":"RATE_LIMITED — usage/plan limit exceeded (see error.retry_after)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"500":{"description":"INTERNAL_ERROR","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"502":{"description":"SOURCE_ERROR — upstream official source returned an error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"503":{"description":"SOURCE_UNAVAILABLE — upstream official source rate limited or unavailable","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}}},"/entities/{nip}/events":{"get":{"operationId":"listEntityEvents","summary":"Entity events","description":"Lists chronological registry change events (VAT, KRS, KRZ) detected for a specific monitored NIP.","parameters":[{"name":"nip","in":"path","required":true,"description":"Polish NIP, 10 digits, checksum-validated (formatting characters are stripped at runtime)","schema":{"type":"string","pattern":"^[0-9]{10}$","description":"Polish NIP, 10 digits, checksum-validated"}},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","minimum":1,"maximum":500,"default":100}},{"name":"cursor","in":"query","required":false,"schema":{"type":"string"},"description":"Opaque keyset cursor from a previous response meta.next_cursor."}],"responses":{"200":{"description":"Events","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/Envelope"},{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Event"}}}}]}}}},"400":{"description":"VALIDATION_ERROR — malformed body/params (includes error.details.issues)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"401":{"description":"UNAUTHORIZED — missing, malformed, revoked or expired API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"FORBIDDEN — API key lacks the required scope, plan limit, or source not on plan","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"404":{"description":"NOT_FOUND — entity is not on this organization watchlist","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"409":{"description":"CONFLICT — Idempotency-Key reused, still processing, outcome indeterminate, or one-time secret cannot be replayed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"description":"RATE_LIMITED — usage/plan limit exceeded (see error.retry_after)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"500":{"description":"INTERNAL_ERROR","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"502":{"description":"SOURCE_ERROR — upstream official source returned an error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"503":{"description":"SOURCE_UNAVAILABLE — upstream official source rate limited or unavailable","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}}},"/entities/{nip}/refresh":{"post":{"operationId":"refreshEntity","summary":"Queue manual refresh","description":"Queues an immediate on-demand registry check across allowed sources (VAT, KRS, KRZ) for a monitored NIP.","parameters":[{"name":"nip","in":"path","required":true,"description":"Polish NIP, 10 digits, checksum-validated (formatting characters are stripped at runtime)","schema":{"type":"string","pattern":"^[0-9]{10}$","description":"Polish NIP, 10 digits, checksum-validated"}},{"name":"Idempotency-Key","in":"header","required":true,"description":"Required for POST/DELETE requests; PATCH does not currently require this header. Printable ASCII, 1-128 characters, not starting with the reserved prefix \"user:\". Reuse only for the exact same method, path and body.","schema":{"type":"string","minLength":1,"maxLength":128,"pattern":"^[ -~]+$"}}],"responses":{"200":{"description":"Queued","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/Envelope"},{"type":"object","properties":{"data":{"$ref":"#/components/schemas/RefreshResponse"}}}]}}}},"400":{"description":"VALIDATION_ERROR — malformed body/params (includes error.details.issues)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"401":{"description":"UNAUTHORIZED — missing, malformed, revoked or expired API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"FORBIDDEN — API key lacks the required scope, plan limit, or source not on plan","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"404":{"description":"NOT_FOUND — entity is not on this organization watchlist","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"409":{"description":"CONFLICT — Idempotency-Key reused, still processing, outcome indeterminate, or one-time secret cannot be replayed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"description":"RATE_LIMITED — usage/plan limit exceeded (see error.retry_after)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"500":{"description":"INTERNAL_ERROR","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"502":{"description":"SOURCE_ERROR — upstream official source returned an error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"503":{"description":"SOURCE_UNAVAILABLE — upstream official source rate limited or unavailable","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}}},"/events":{"get":{"operationId":"listEvents","summary":"Organization event feed","description":"Lists recent registry risk events across all NIPs monitored by the organization.","parameters":[{"name":"limit","in":"query","required":false,"schema":{"type":"integer","minimum":1,"maximum":500,"default":100}},{"name":"cursor","in":"query","required":false,"schema":{"type":"string"},"description":"Opaque keyset cursor from a previous response meta.next_cursor."},{"name":"severity","in":"query","required":false,"style":"form","explode":false,"description":"Comma-separated severities.","schema":{"type":"array","items":{"enum":["info","warning","critical"]}}},{"name":"source","in":"query","required":false,"style":"form","explode":false,"description":"Comma-separated sources.","schema":{"type":"array","items":{"enum":["VAT","KRS","KRZ"]}}},{"name":"event_type","in":"query","required":false,"style":"form","explode":false,"description":"Comma-separated event types (closed list).","schema":{"type":"array","items":{"enum":["VAT_REMOVED","VAT_NOT_REGISTERED","VAT_RESTORED","VAT_STATUS_CHANGED","VAT_BANK_ACCOUNTS_CHANGED","KRS_DELETED","KRS_BANKRUPTCY","KRS_LIQUIDATION","KRS_RESTRUCTURING","KRS_BOARD_CHANGED","KRS_ADDRESS_CHANGED","KRZ_BANKRUPTCY_FOUND","KRZ_RESTRUCTURING_FOUND","KRZ_BUSINESS_BAN_FOUND","KRZ_NEW_PROCEEDING","KRZ_ENFORCEMENT_FOUND","KRZ_PROCEEDING_CLOSED","KRS_REACTIVATED"]}}},{"name":"since","in":"query","required":false,"schema":{"type":"string","format":"date-time"},"description":"detected_at >= since (ISO 8601 with offset)."},{"name":"until","in":"query","required":false,"schema":{"type":"string","format":"date-time"},"description":"detected_at < until; must be after since."},{"name":"resolved","in":"query","required":false,"schema":{"enum":["true","false"]},"description":"true: resolved events only; false: open events only."}],"responses":{"200":{"description":"Events","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/Envelope"},{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Event"}}}}]}}}},"400":{"description":"VALIDATION_ERROR — malformed body/params (includes error.details.issues)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"401":{"description":"UNAUTHORIZED — missing, malformed, revoked or expired API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"FORBIDDEN — API key lacks the required scope, plan limit, or source not on plan","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"404":{"description":"NOT_FOUND — entity is not on this organization watchlist","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"409":{"description":"CONFLICT — Idempotency-Key reused, still processing, outcome indeterminate, or one-time secret cannot be replayed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"description":"RATE_LIMITED — usage/plan limit exceeded (see error.retry_after)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"500":{"description":"INTERNAL_ERROR","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"502":{"description":"SOURCE_ERROR — upstream official source returned an error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"503":{"description":"SOURCE_UNAVAILABLE — upstream official source rate limited or unavailable","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}}},"/webhooks":{"get":{"operationId":"listWebhooks","summary":"List webhooks","description":"Lists configured HTTPS webhook endpoints for the organization.","responses":{"200":{"description":"Webhooks","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/Envelope"},{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Webhook"}}}}]}}}},"400":{"description":"VALIDATION_ERROR — malformed body/params (includes error.details.issues)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"401":{"description":"UNAUTHORIZED — missing, malformed, revoked or expired API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"FORBIDDEN — API key lacks the required scope, plan limit, or source not on plan","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"404":{"description":"NOT_FOUND — entity is not on this organization watchlist","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"409":{"description":"CONFLICT — Idempotency-Key reused, still processing, outcome indeterminate, or one-time secret cannot be replayed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"description":"RATE_LIMITED — usage/plan limit exceeded (see error.retry_after)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"500":{"description":"INTERNAL_ERROR","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"502":{"description":"SOURCE_ERROR — upstream official source returned an error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"503":{"description":"SOURCE_UNAVAILABLE — upstream official source rate limited or unavailable","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}},"post":{"operationId":"createWebhook","summary":"Create webhook","description":"Registers a new HTTPS webhook endpoint and returns its one-time HMAC-SHA256 signing secret.","parameters":[{"name":"Idempotency-Key","in":"header","required":true,"description":"Required for POST/DELETE requests; PATCH does not currently require this header. Printable ASCII, 1-128 characters, not starting with the reserved prefix \"user:\". Reuse only for the exact same method, path and body.","schema":{"type":"string","minLength":1,"maxLength":128,"pattern":"^[ -~]+$"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookCreateRequest"}}}},"responses":{"200":{"description":"Webhook with one-time secret","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/Envelope"},{"type":"object","properties":{"data":{"$ref":"#/components/schemas/WebhookCreated"}}}]}}}},"400":{"description":"VALIDATION_ERROR — malformed body/params (includes error.details.issues)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"401":{"description":"UNAUTHORIZED — missing, malformed, revoked or expired API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"FORBIDDEN — API key lacks the required scope, plan limit, or source not on plan","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"404":{"description":"NOT_FOUND — entity is not on this organization watchlist","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"409":{"description":"CONFLICT — Idempotency-Key reused, still processing, outcome indeterminate, or one-time secret cannot be replayed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"description":"RATE_LIMITED — usage/plan limit exceeded (see error.retry_after)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"500":{"description":"INTERNAL_ERROR","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"502":{"description":"SOURCE_ERROR — upstream official source returned an error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"503":{"description":"SOURCE_UNAVAILABLE — upstream official source rate limited or unavailable","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}}},"/webhooks/{id}":{"delete":{"operationId":"deleteWebhook","summary":"Delete webhook","description":"Deletes a webhook endpoint by UUID.","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}},{"name":"Idempotency-Key","in":"header","required":true,"description":"Required for POST/DELETE requests; PATCH does not currently require this header. Printable ASCII, 1-128 characters, not starting with the reserved prefix \"user:\". Reuse only for the exact same method, path and body.","schema":{"type":"string","minLength":1,"maxLength":128,"pattern":"^[ -~]+$"}}],"responses":{"200":{"description":"Deleted","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/Envelope"},{"type":"object","properties":{"data":{"type":"object","required":["deleted"],"properties":{"deleted":{"type":"string","format":"uuid"}}}}}]}}}},"400":{"description":"VALIDATION_ERROR — malformed body/params (includes error.details.issues)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"401":{"description":"UNAUTHORIZED — missing, malformed, revoked or expired API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"FORBIDDEN — API key lacks the required scope, plan limit, or source not on plan","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"404":{"description":"NOT_FOUND — entity is not on this organization watchlist","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"409":{"description":"CONFLICT — Idempotency-Key reused, still processing, outcome indeterminate, or one-time secret cannot be replayed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"description":"RATE_LIMITED — usage/plan limit exceeded (see error.retry_after)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"500":{"description":"INTERNAL_ERROR","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"502":{"description":"SOURCE_ERROR — upstream official source returned an error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"503":{"description":"SOURCE_UNAVAILABLE — upstream official source rate limited or unavailable","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}}},"/webhooks/{id}/deliveries":{"get":{"operationId":"listWebhookDeliveries","summary":"Webhook deliveries","description":"Lists recent delivery attempts and HTTP status codes for a specific webhook UUID.","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","minimum":1,"maximum":500,"default":100}},{"name":"cursor","in":"query","required":false,"schema":{"type":"string"},"description":"Opaque keyset cursor from a previous response meta.next_cursor."}],"responses":{"200":{"description":"Deliveries","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/Envelope"},{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/WebhookDelivery"}}}}]}}}},"400":{"description":"VALIDATION_ERROR — malformed body/params (includes error.details.issues)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"401":{"description":"UNAUTHORIZED — missing, malformed, revoked or expired API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"FORBIDDEN — API key lacks the required scope, plan limit, or source not on plan","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"404":{"description":"NOT_FOUND — entity is not on this organization watchlist","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"409":{"description":"CONFLICT — Idempotency-Key reused, still processing, outcome indeterminate, or one-time secret cannot be replayed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"description":"RATE_LIMITED — usage/plan limit exceeded (see error.retry_after)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"500":{"description":"INTERNAL_ERROR","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"502":{"description":"SOURCE_ERROR — upstream official source returned an error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"503":{"description":"SOURCE_UNAVAILABLE — upstream official source rate limited or unavailable","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}}},"/webhooks/{id}/deliveries/{deliveryId}/replay":{"post":{"operationId":"replayWebhookDelivery","summary":"Replay webhook delivery","description":"Requeues one failed or delivered delivery of an active webhook (status pending, attempts 0, due now). It is re-signed with a fresh timestamp and keeps its delivery id. 409 if the delivery is pending/sending or the webhook is inactive. Requires the webhooks scope.","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}},{"name":"deliveryId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}},{"name":"Idempotency-Key","in":"header","required":true,"description":"Required for POST/DELETE requests; PATCH does not currently require this header. Printable ASCII, 1-128 characters, not starting with the reserved prefix \"user:\". Reuse only for the exact same method, path and body.","schema":{"type":"string","minLength":1,"maxLength":128,"pattern":"^[ -~]+$"}}],"responses":{"200":{"description":"Delivery requeued","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/Envelope"},{"type":"object","properties":{"data":{"$ref":"#/components/schemas/WebhookReplayResult"}}}]}}}},"400":{"description":"VALIDATION_ERROR — malformed body/params (includes error.details.issues)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"401":{"description":"UNAUTHORIZED — missing, malformed, revoked or expired API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"FORBIDDEN — API key lacks the required scope, plan limit, or source not on plan","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"404":{"description":"NOT_FOUND — entity is not on this organization watchlist","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"409":{"description":"CONFLICT — Idempotency-Key reused, still processing, outcome indeterminate, or one-time secret cannot be replayed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"description":"RATE_LIMITED — usage/plan limit exceeded (see error.retry_after)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"500":{"description":"INTERNAL_ERROR","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"502":{"description":"SOURCE_ERROR — upstream official source returned an error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"503":{"description":"SOURCE_UNAVAILABLE — upstream official source rate limited or unavailable","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}}},"/webhooks/{id}/replay-failed":{"post":{"operationId":"replayFailedWebhookDeliveries","summary":"Replay failed webhook deliveries","description":"Requeues failed deliveries of an active webhook created in the since..until window (max 7 days), at most 500 per call (oldest first). Requires the webhooks scope.","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}},{"name":"Idempotency-Key","in":"header","required":true,"description":"Required for POST/DELETE requests; PATCH does not currently require this header. Printable ASCII, 1-128 characters, not starting with the reserved prefix \"user:\". Reuse only for the exact same method, path and body.","schema":{"type":"string","minLength":1,"maxLength":128,"pattern":"^[ -~]+$"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookReplayFailedRequest"}}}},"responses":{"200":{"description":"Requeue result","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/Envelope"},{"type":"object","properties":{"data":{"$ref":"#/components/schemas/WebhookReplayFailedResult"}}}]}}}},"400":{"description":"VALIDATION_ERROR — malformed body/params (includes error.details.issues)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"401":{"description":"UNAUTHORIZED — missing, malformed, revoked or expired API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"FORBIDDEN — API key lacks the required scope, plan limit, or source not on plan","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"404":{"description":"NOT_FOUND — entity is not on this organization watchlist","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"409":{"description":"CONFLICT — Idempotency-Key reused, still processing, outcome indeterminate, or one-time secret cannot be replayed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"description":"RATE_LIMITED — usage/plan limit exceeded (see error.retry_after)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"500":{"description":"INTERNAL_ERROR","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"502":{"description":"SOURCE_ERROR — upstream official source returned an error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"503":{"description":"SOURCE_UNAVAILABLE — upstream official source rate limited or unavailable","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}}},"/webhooks/{id}/rotate-secret":{"post":{"operationId":"rotateWebhookSecret","summary":"Rotate webhook signing secret","description":"Generates a new signing secret for an active webhook and returns it once. The previous secret keeps signing for grace_hours (default 24, max 168): during that window every delivery carries two v1= signatures in X-RiskWatch-Signature, so a receiver holding either secret verifies. Only one previous secret is kept; rotating again replaces it. 404 if the webhook is not active in this organization; 503 while the server is being upgraded. Requires the webhooks scope; rw_test keys get 403.","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}},{"name":"Idempotency-Key","in":"header","required":true,"description":"Required for POST/DELETE requests; PATCH does not currently require this header. Printable ASCII, 1-128 characters, not starting with the reserved prefix \"user:\". Reuse only for the exact same method, path and body.","schema":{"type":"string","minLength":1,"maxLength":128,"pattern":"^[ -~]+$"}}],"requestBody":{"required":false,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookRotateSecretRequest"}}}},"responses":{"200":{"description":"New secret (shown once)","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/Envelope"},{"type":"object","properties":{"data":{"$ref":"#/components/schemas/WebhookSecretRotated"}}}]}}}},"400":{"description":"VALIDATION_ERROR — malformed body/params (includes error.details.issues)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"401":{"description":"UNAUTHORIZED — missing, malformed, revoked or expired API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"FORBIDDEN — API key lacks the required scope, plan limit, or source not on plan","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"404":{"description":"NOT_FOUND — webhook not found or inactive","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"409":{"description":"CONFLICT — Idempotency-Key reused, still processing, outcome indeterminate, or one-time secret cannot be replayed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"description":"RATE_LIMITED — usage/plan limit exceeded (see error.retry_after)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"500":{"description":"INTERNAL_ERROR","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"502":{"description":"SOURCE_ERROR — upstream official source returned an error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"503":{"description":"SOURCE_UNAVAILABLE — rotation temporarily unavailable during an upgrade (see error.retry_after)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}}},"/usage":{"get":{"operationId":"getUsage","summary":"Usage counters","description":"Returns current billing-period and daily usage counters against plan limits.","responses":{"200":{"description":"Usage","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/Envelope"},{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/UsageCounter"}}}}]}}}},"400":{"description":"VALIDATION_ERROR — malformed body/params (includes error.details.issues)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"401":{"description":"UNAUTHORIZED — missing, malformed, revoked or expired API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"FORBIDDEN — API key lacks the required scope, plan limit, or source not on plan","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"404":{"description":"NOT_FOUND — entity is not on this organization watchlist","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"409":{"description":"CONFLICT — Idempotency-Key reused, still processing, outcome indeterminate, or one-time secret cannot be replayed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"description":"RATE_LIMITED — usage/plan limit exceeded (see error.retry_after)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"500":{"description":"INTERNAL_ERROR","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"502":{"description":"SOURCE_ERROR — upstream official source returned an error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"503":{"description":"SOURCE_UNAVAILABLE — upstream official source rate limited or unavailable","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}}},"/checks/vat":{"post":{"operationId":"checkVatBatch","summary":"One-shot VAT whitelist check (up to 30 NIPs)","description":"Checks up to 30 Polish NIPs against the MF VAT whitelist in one upstream request without adding them to the watchlist. Metered as one API request. Requires the read scope.","parameters":[{"name":"Idempotency-Key","in":"header","required":true,"description":"Required for POST/DELETE requests; PATCH does not currently require this header. Printable ASCII, 1-128 characters, not starting with the reserved prefix \"user:\". Reuse only for the exact same method, path and body.","schema":{"type":"string","minLength":1,"maxLength":128,"pattern":"^[ -~]+$"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CheckVatRequest"}}}},"responses":{"200":{"description":"VAT whitelist results","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/Envelope"},{"type":"object","properties":{"data":{"$ref":"#/components/schemas/CheckVatResponse"}}}]}}}},"400":{"description":"VALIDATION_ERROR — malformed body/params (includes error.details.issues)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"401":{"description":"UNAUTHORIZED — missing, malformed, revoked or expired API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"FORBIDDEN — API key lacks the required scope, plan limit, or source not on plan","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"404":{"description":"NOT_FOUND — entity is not on this organization watchlist","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"409":{"description":"CONFLICT — Idempotency-Key reused, still processing, outcome indeterminate, or one-time secret cannot be replayed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"description":"RATE_LIMITED — usage/plan limit exceeded, or the shared daily Ministry of Finance whitelist budget for one-shot checks is exhausted (resets at midnight Europe/Warsaw; see error.retry_after). Watchlist monitoring is unaffected.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"500":{"description":"INTERNAL_ERROR","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"502":{"description":"SOURCE_ERROR — upstream official source returned an error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"503":{"description":"SOURCE_UNAVAILABLE — upstream MF whitelist rate limited or unavailable, or the daily MF budget counter is unavailable (fail-closed)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}}},"/checks/bank-account":{"post":{"operationId":"checkBankAccount","summary":"Payment-time bank account check","description":"Checks whether a bank account (NRB) is on the MF VAT whitelist for a NIP on a given date, using the MF check method. The account is never echoed in full. Metered as one API request. Requires the read scope.","parameters":[{"name":"Idempotency-Key","in":"header","required":true,"description":"Required for POST/DELETE requests; PATCH does not currently require this header. Printable ASCII, 1-128 characters, not starting with the reserved prefix \"user:\". Reuse only for the exact same method, path and body.","schema":{"type":"string","minLength":1,"maxLength":128,"pattern":"^[ -~]+$"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CheckBankAccountRequest"}}}},"responses":{"200":{"description":"Account assignment verdict","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/Envelope"},{"type":"object","properties":{"data":{"$ref":"#/components/schemas/CheckBankAccountResponse"}}}]}}}},"400":{"description":"VALIDATION_ERROR — malformed body/params (includes error.details.issues)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"401":{"description":"UNAUTHORIZED — missing, malformed, revoked or expired API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"FORBIDDEN — API key lacks the required scope, plan limit, or source not on plan","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"404":{"description":"NOT_FOUND — entity is not on this organization watchlist","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"409":{"description":"CONFLICT — Idempotency-Key reused, still processing, outcome indeterminate, or one-time secret cannot be replayed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"description":"RATE_LIMITED — usage/plan limit exceeded, or the shared daily Ministry of Finance whitelist budget for one-shot checks is exhausted (resets at midnight Europe/Warsaw; see error.retry_after). Watchlist monitoring is unaffected.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"500":{"description":"INTERNAL_ERROR","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"502":{"description":"SOURCE_ERROR — upstream official source returned an error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"503":{"description":"SOURCE_UNAVAILABLE — upstream MF whitelist rate limited or unavailable, or the daily MF budget counter is unavailable (fail-closed)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}}},"/openapi.json":{"get":{"operationId":"getOpenApiSpec","security":[],"summary":"OpenAPI specification","description":"Returns the OpenAPI 3.1 JSON contract for RiskWatch API v1.","responses":{"200":{"description":"OpenAPI JSON","content":{"application/json":{"schema":{"type":"object","description":"This OpenAPI document itself (not wrapped in the {data, meta} envelope)."}}}}}}},"/entities/{nip}/risk/history":{"get":{"operationId":"listEntityRiskHistory","summary":"Entity risk score history","description":"Lists distinct computed risk states for a monitored NIP, newest first (a row is stored only when score, band or reasons changed). Cursor-paginated. 404 if the NIP is not on the organization watchlist; 503 until score history is enabled. Sandbox keys get fixture history. Requires the read scope.","parameters":[{"name":"nip","in":"path","required":true,"description":"Polish NIP, 10 digits, checksum-validated (formatting characters are stripped at runtime)","schema":{"type":"string","pattern":"^[0-9]{10}$","description":"Polish NIP, 10 digits, checksum-validated"}},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","minimum":1,"maximum":500,"default":100}},{"name":"cursor","in":"query","required":false,"schema":{"type":"string"},"description":"Opaque keyset cursor from a previous response meta.next_cursor."}],"responses":{"200":{"description":"History page","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/Envelope"},{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/RiskScoreHistoryEntry"}}}}]}}}},"400":{"description":"VALIDATION_ERROR — malformed body/params (includes error.details.issues)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"401":{"description":"UNAUTHORIZED — missing, malformed, revoked or expired API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"FORBIDDEN — API key lacks the required scope, plan limit, or source not on plan","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"404":{"description":"NOT_FOUND — entity is not on this organization watchlist","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"409":{"description":"CONFLICT — Idempotency-Key reused, still processing, outcome indeterminate, or one-time secret cannot be replayed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"description":"RATE_LIMITED — usage/plan limit exceeded (see error.retry_after)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"500":{"description":"INTERNAL_ERROR","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"502":{"description":"SOURCE_ERROR — upstream official source returned an error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"503":{"description":"SOURCE_UNAVAILABLE — upstream official source rate limited or unavailable","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}}},"/entities/{nip}/risk/explain":{"get":{"operationId":"explainEntityRiskChange","summary":"Explain a risk score change","description":"Returns the stored risk snapshots in effect at `from` and at `to` (default now) and a deterministic diff: reasons added, removed and with changed impact, each with the event that caused it. 404 if the NIP is not on the organization watchlist; 503 until score history is enabled. Requires the read scope.","parameters":[{"name":"nip","in":"path","required":true,"description":"Polish NIP, 10 digits, checksum-validated (formatting characters are stripped at runtime)","schema":{"type":"string","pattern":"^[0-9]{10}$","description":"Polish NIP, 10 digits, checksum-validated"}},{"name":"from","in":"query","required":true,"schema":{"type":"string","format":"date-time"},"description":"ISO 8601 instant with offset."},{"name":"to","in":"query","required":false,"schema":{"type":"string","format":"date-time"},"description":"ISO 8601 instant with offset, after `from`. Defaults to now."}],"responses":{"200":{"description":"Explanation","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/Envelope"},{"type":"object","properties":{"data":{"$ref":"#/components/schemas/RiskChangeExplanation"}}}]}}}},"400":{"description":"VALIDATION_ERROR — malformed body/params (includes error.details.issues)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"401":{"description":"UNAUTHORIZED — missing, malformed, revoked or expired API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"FORBIDDEN — API key lacks the required scope, plan limit, or source not on plan","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"404":{"description":"NOT_FOUND — entity is not on this organization watchlist","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"409":{"description":"CONFLICT — Idempotency-Key reused, still processing, outcome indeterminate, or one-time secret cannot be replayed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"description":"RATE_LIMITED — usage/plan limit exceeded (see error.retry_after)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"500":{"description":"INTERNAL_ERROR","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"502":{"description":"SOURCE_ERROR — upstream official source returned an error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"503":{"description":"SOURCE_UNAVAILABLE — upstream official source rate limited or unavailable","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}}},"/portfolio/summary":{"get":{"operationId":"getPortfolioSummary","summary":"Portfolio risk summary","description":"One aggregate over the active watchlist: counts by risk band (with UNSCORED), band by monitored source, open events by source and severity, and NIPs whose score rose in the last 7 days. Requires the read scope.","responses":{"200":{"description":"Summary","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/Envelope"},{"type":"object","properties":{"data":{"$ref":"#/components/schemas/PortfolioSummary"}}}]}}}},"400":{"description":"VALIDATION_ERROR — malformed body/params (includes error.details.issues)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"401":{"description":"UNAUTHORIZED — missing, malformed, revoked or expired API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"FORBIDDEN — API key lacks the required scope, plan limit, or source not on plan","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"404":{"description":"NOT_FOUND — entity is not on this organization watchlist","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"409":{"description":"CONFLICT — Idempotency-Key reused, still processing, outcome indeterminate, or one-time secret cannot be replayed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"description":"RATE_LIMITED — usage/plan limit exceeded (see error.retry_after)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"500":{"description":"INTERNAL_ERROR","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"502":{"description":"SOURCE_ERROR — upstream official source returned an error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"503":{"description":"SOURCE_UNAVAILABLE — upstream official source rate limited or unavailable","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}}},"/watchlist/{nip}":{"patch":{"operationId":"setWatchlistReviewInterval","summary":"Set review interval","description":"Sets (or clears with null) the periodic review interval in days for one monitored NIP, for an ongoing-monitoring calendar such as art. 34 ust. 1 pkt 4 of the Polish AML act. The interval is your policy, not a RiskWatch default. Requires the write scope; idempotent by value. 503 until the review calendar migration is applied. rw_test keys get a fixture for the sandbox NIPs.","parameters":[{"name":"nip","in":"path","required":true,"description":"Polish NIP, 10 digits, checksum-validated (formatting characters are stripped at runtime)","schema":{"type":"string","pattern":"^[0-9]{10}$","description":"Polish NIP, 10 digits, checksum-validated"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ReviewIntervalRequest"}}}},"responses":{"200":{"description":"Review state","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/Envelope"},{"type":"object","properties":{"data":{"$ref":"#/components/schemas/ReviewState"}}}]}}}},"400":{"description":"VALIDATION_ERROR — malformed body/params (includes error.details.issues)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"401":{"description":"UNAUTHORIZED — missing, malformed, revoked or expired API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"FORBIDDEN — API key lacks the required scope, plan limit, or source not on plan","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"404":{"description":"NOT_FOUND — entity is not on this organization watchlist","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"409":{"description":"CONFLICT — Idempotency-Key reused, still processing, outcome indeterminate, or one-time secret cannot be replayed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"description":"RATE_LIMITED — usage/plan limit exceeded (see error.retry_after)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"500":{"description":"INTERNAL_ERROR","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"502":{"description":"SOURCE_ERROR — upstream official source returned an error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"503":{"description":"SOURCE_UNAVAILABLE — upstream official source rate limited or unavailable","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}}},"/watchlist/{nip}/review":{"post":{"operationId":"acknowledgeWatchlistReview","summary":"Acknowledge a review","description":"Records that the counterparty file was reviewed now: stamps last_reviewed_at and writes an audit log entry (with the optional note). Events are not changed. Requires the write scope. rw_test keys get a fixture and nothing is stored.","parameters":[{"name":"nip","in":"path","required":true,"description":"Polish NIP, 10 digits, checksum-validated (formatting characters are stripped at runtime)","schema":{"type":"string","pattern":"^[0-9]{10}$","description":"Polish NIP, 10 digits, checksum-validated"}},{"name":"Idempotency-Key","in":"header","required":true,"description":"Required for POST/DELETE requests; PATCH does not currently require this header. Printable ASCII, 1-128 characters, not starting with the reserved prefix \"user:\". Reuse only for the exact same method, path and body.","schema":{"type":"string","minLength":1,"maxLength":128,"pattern":"^[ -~]+$"}}],"requestBody":{"required":false,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ReviewAcknowledgeRequest"}}}},"responses":{"200":{"description":"Review state","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/Envelope"},{"type":"object","properties":{"data":{"$ref":"#/components/schemas/ReviewState"}}}]}}}},"400":{"description":"VALIDATION_ERROR — malformed body/params (includes error.details.issues)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"401":{"description":"UNAUTHORIZED — missing, malformed, revoked or expired API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"FORBIDDEN — API key lacks the required scope, plan limit, or source not on plan","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"404":{"description":"NOT_FOUND — entity is not on this organization watchlist","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"409":{"description":"CONFLICT — Idempotency-Key reused, still processing, outcome indeterminate, or one-time secret cannot be replayed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"description":"RATE_LIMITED — usage/plan limit exceeded (see error.retry_after)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"500":{"description":"INTERNAL_ERROR","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"502":{"description":"SOURCE_ERROR — upstream official source returned an error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"503":{"description":"SOURCE_UNAVAILABLE — upstream official source rate limited or unavailable","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}}},"/reviews/due":{"get":{"operationId":"listReviewsDue","summary":"Reviews due","description":"Lists monitored NIPs whose review is due: last_reviewed_at (or the date added, if never reviewed) plus review_interval_days is now or earlier, oldest first, at most limit rows. Items without an interval never appear. Requires the read scope.","parameters":[{"name":"limit","in":"query","required":false,"schema":{"type":"integer","minimum":1,"maximum":500,"default":100}}],"responses":{"200":{"description":"Due reviews","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/Envelope"},{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/ReviewDueItem"}}}}]}}}},"400":{"description":"VALIDATION_ERROR — malformed body/params (includes error.details.issues)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"401":{"description":"UNAUTHORIZED — missing, malformed, revoked or expired API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"FORBIDDEN — API key lacks the required scope, plan limit, or source not on plan","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"404":{"description":"NOT_FOUND — entity is not on this organization watchlist","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"409":{"description":"CONFLICT — Idempotency-Key reused, still processing, outcome indeterminate, or one-time secret cannot be replayed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"description":"RATE_LIMITED — usage/plan limit exceeded (see error.retry_after)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"500":{"description":"INTERNAL_ERROR","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"502":{"description":"SOURCE_ERROR — upstream official source returned an error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"503":{"description":"SOURCE_UNAVAILABLE — upstream official source rate limited or unavailable","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}}},"/watchlist/imports":{"post":{"operationId":"createWatchlistImport","summary":"Start an asynchronous bulk watchlist import","description":"Accepts text/csv (header row nip[,label][,external_id]; comma or semicolon delimited) or JSON {items:[...]} / {csv:string} with up to 25,000 rows and answers 202 with a job; poll GET /watchlist/imports/{id}. The whole import is rejected up front (403) when it cannot fit the plan watchlist limit. Rows are added through the same path as POST /watchlist. XLSX is not accepted: export the sheet as CSV. Sandbox (rw_test) keys get 403. Requires the write scope.","parameters":[{"name":"Idempotency-Key","in":"header","required":true,"description":"Required for POST/DELETE requests; PATCH does not currently require this header. Printable ASCII, 1-128 characters, not starting with the reserved prefix \"user:\". Reuse only for the exact same method, path and body.","schema":{"type":"string","minLength":1,"maxLength":128,"pattern":"^[ -~]+$"}}],"requestBody":{"required":true,"content":{"text/csv":{"schema":{"type":"string"}},"application/json":{"schema":{"oneOf":[{"$ref":"#/components/schemas/ImportJsonRequest"},{"type":"object","required":["csv"],"additionalProperties":false,"properties":{"csv":{"type":"string","minLength":1,"description":"CSV header nip[,label][,external_id], up to 25,000 data records"},"sources":{"type":"array","minItems":1,"items":{"enum":["VAT","KRS","KRZ"]},"default":["VAT"]},"check_frequency":{"enum":["daily","weekly","manual"],"default":"daily"}}}]}}}},"responses":{"202":{"description":"Import job queued (the Location header points to the job)","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/Envelope"},{"type":"object","properties":{"data":{"$ref":"#/components/schemas/ImportJob"}}}]}}}},"400":{"description":"VALIDATION_ERROR — malformed body/params (includes error.details.issues)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"401":{"description":"UNAUTHORIZED — missing, malformed, revoked or expired API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"FORBIDDEN — API key lacks the required scope, plan limit, or source not on plan","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"404":{"description":"NOT_FOUND — entity is not on this organization watchlist","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"409":{"description":"CONFLICT — Idempotency-Key reused, still processing, outcome indeterminate, or one-time secret cannot be replayed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"description":"RATE_LIMITED — usage/plan limit exceeded (see error.retry_after)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"500":{"description":"INTERNAL_ERROR","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"502":{"description":"SOURCE_ERROR — upstream official source returned an error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"503":{"description":"SOURCE_UNAVAILABLE — upstream official source rate limited or unavailable","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}}},"/watchlist/imports/{id}":{"get":{"operationId":"getWatchlistImport","summary":"Bulk import status","description":"Returns the status, totals and the first 1000 per-row errors (row number and reason only) of an import job of this organization. Sandbox (rw_test) keys get 403.","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Import job","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/Envelope"},{"type":"object","properties":{"data":{"$ref":"#/components/schemas/ImportJob"}}}]}}}},"400":{"description":"VALIDATION_ERROR — malformed body/params (includes error.details.issues)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"401":{"description":"UNAUTHORIZED — missing, malformed, revoked or expired API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"FORBIDDEN — API key lacks the required scope, plan limit, or source not on plan","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"404":{"description":"NOT_FOUND — entity is not on this organization watchlist","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"409":{"description":"CONFLICT — Idempotency-Key reused, still processing, outcome indeterminate, or one-time secret cannot be replayed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"description":"RATE_LIMITED — usage/plan limit exceeded (see error.retry_after)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"500":{"description":"INTERNAL_ERROR","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"502":{"description":"SOURCE_ERROR — upstream official source returned an error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"503":{"description":"SOURCE_UNAVAILABLE — upstream official source rate limited or unavailable","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}}},"/watchlist/lookup":{"post":{"operationId":"lookupWatchlistByExternalId","summary":"Look up watchlist rows by external_id","description":"Maps up to 500 external_id values to the organization watchlist rows that carry them. Writes nothing, so a read-scoped key may call it. Sandbox fixtures carry no external_id, so a sandbox key gets every id back in not_found.","parameters":[{"name":"Idempotency-Key","in":"header","required":true,"description":"Required for POST/DELETE requests; PATCH does not currently require this header. Printable ASCII, 1-128 characters, not starting with the reserved prefix \"user:\". Reuse only for the exact same method, path and body.","schema":{"type":"string","minLength":1,"maxLength":128,"pattern":"^[ -~]+$"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WatchlistLookupRequest"}}}},"responses":{"200":{"description":"Lookup result","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/Envelope"},{"type":"object","properties":{"data":{"$ref":"#/components/schemas/WatchlistLookupResult"}}}]}}}},"400":{"description":"VALIDATION_ERROR — malformed body/params (includes error.details.issues)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"401":{"description":"UNAUTHORIZED — missing, malformed, revoked or expired API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"FORBIDDEN — API key lacks the required scope, plan limit, or source not on plan","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"404":{"description":"NOT_FOUND — entity is not on this organization watchlist","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"409":{"description":"CONFLICT — Idempotency-Key reused, still processing, outcome indeterminate, or one-time secret cannot be replayed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"description":"RATE_LIMITED — usage/plan limit exceeded (see error.retry_after)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"500":{"description":"INTERNAL_ERROR","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"502":{"description":"SOURCE_ERROR — upstream official source returned an error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"503":{"description":"SOURCE_UNAVAILABLE — upstream official source rate limited or unavailable","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}}},"/entities/search":{"get":{"operationId":"searchEntities","summary":"Search monitored entities by name","description":"Case-insensitive name fragment search over this organization watchlist only (never the public registers). q is 3-80 characters; % and _ match literally; at most 50 results. Sandbox keys search the fixtures.","parameters":[{"name":"q","in":"query","required":true,"schema":{"type":"string","minLength":3,"maxLength":80}}],"responses":{"200":{"description":"Matching entities","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/Envelope"},{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/EntityStatus"}}}}]}}}},"400":{"description":"VALIDATION_ERROR — malformed body/params (includes error.details.issues)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"401":{"description":"UNAUTHORIZED — missing, malformed, revoked or expired API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"FORBIDDEN — API key lacks the required scope, plan limit, or source not on plan","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"404":{"description":"NOT_FOUND — entity is not on this organization watchlist","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"409":{"description":"CONFLICT — Idempotency-Key reused, still processing, outcome indeterminate, or one-time secret cannot be replayed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"description":"RATE_LIMITED — usage/plan limit exceeded (see error.retry_after)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"500":{"description":"INTERNAL_ERROR","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"502":{"description":"SOURCE_ERROR — upstream official source returned an error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"503":{"description":"SOURCE_UNAVAILABLE — upstream official source rate limited or unavailable","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}}},"/events/export":{"get":{"operationId":"exportEvents","summary":"Export the event feed (CSV or JSONL)","description":"Streams up to 100,000 events of this organization, newest first, as text/csv (default) or application/x-ndjson, with the same filters as GET /events. CSV cells starting with = + - @ TAB or CR are prefixed with an apostrophe. PDF is not offered. Sandbox (rw_test) keys get 403.","parameters":[{"name":"format","in":"query","required":false,"schema":{"enum":["csv","jsonl"],"default":"csv"}},{"name":"severity","in":"query","required":false,"style":"form","explode":false,"description":"Comma-separated severities.","schema":{"type":"array","items":{"enum":["info","warning","critical"]}}},{"name":"source","in":"query","required":false,"style":"form","explode":false,"description":"Comma-separated sources.","schema":{"type":"array","items":{"enum":["VAT","KRS","KRZ"]}}},{"name":"event_type","in":"query","required":false,"style":"form","explode":false,"description":"Comma-separated event types (closed list).","schema":{"type":"array","items":{"enum":["VAT_REMOVED","VAT_NOT_REGISTERED","VAT_RESTORED","VAT_STATUS_CHANGED","VAT_BANK_ACCOUNTS_CHANGED","KRS_DELETED","KRS_BANKRUPTCY","KRS_LIQUIDATION","KRS_RESTRUCTURING","KRS_BOARD_CHANGED","KRS_ADDRESS_CHANGED","KRZ_BANKRUPTCY_FOUND","KRZ_RESTRUCTURING_FOUND","KRZ_BUSINESS_BAN_FOUND","KRZ_NEW_PROCEEDING","KRZ_ENFORCEMENT_FOUND","KRZ_PROCEEDING_CLOSED","KRS_REACTIVATED"]}}},{"name":"since","in":"query","required":false,"schema":{"type":"string","format":"date-time"},"description":"detected_at >= since (ISO 8601 with offset)."},{"name":"until","in":"query","required":false,"schema":{"type":"string","format":"date-time"},"description":"detected_at < until; must be after since."},{"name":"resolved","in":"query","required":false,"schema":{"enum":["true","false"]},"description":"true: resolved events only; false: open events only."}],"responses":{"200":{"description":"Attachment","content":{"text/csv":{"schema":{"type":"string"}},"application/x-ndjson":{"schema":{"type":"string"}}}},"400":{"description":"VALIDATION_ERROR — malformed body/params (includes error.details.issues)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"401":{"description":"UNAUTHORIZED — missing, malformed, revoked or expired API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"FORBIDDEN — API key lacks the read scope, or the key is a sandbox (rw_test) key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"description":"RATE_LIMITED — usage/plan limit exceeded (see error.retry_after)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"500":{"description":"INTERNAL_ERROR","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}}},"/watchlist/export":{"get":{"operationId":"exportWatchlist","summary":"Export the watchlist (CSV)","description":"Streams up to 100,000 watchlist rows of this organization as text/csv with the risk band and score, with the same filters as GET /watchlist. Formula-injection guarded like the events export. Sandbox (rw_test) keys get 403.","parameters":[{"name":"format","in":"query","required":false,"schema":{"enum":["csv"],"default":"csv"}},{"name":"band","in":"query","required":false,"style":"form","explode":false,"description":"Comma-separated risk bands; only entities with a computed score match.","schema":{"type":"array","items":{"enum":["LOW","MEDIUM","HIGH","CRITICAL"]}}},{"name":"status","in":"query","required":false,"schema":{"enum":["active","paused"]}},{"name":"source","in":"query","required":false,"schema":{"enum":["VAT","KRS","KRZ"]},"description":"Rows monitoring this source."},{"name":"label_prefix","in":"query","required":false,"schema":{"type":"string","minLength":1,"maxLength":160},"description":"Case-insensitive label prefix; % and _ match literally, * is rejected."},{"name":"external_id","in":"query","required":false,"schema":{"type":"string","minLength":1,"maxLength":160},"description":"Exact external_id match (not unique: several rows may share one)."}],"responses":{"200":{"description":"Attachment","content":{"text/csv":{"schema":{"type":"string"}}}},"400":{"description":"VALIDATION_ERROR — malformed body/params (includes error.details.issues)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"401":{"description":"UNAUTHORIZED — missing, malformed, revoked or expired API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"FORBIDDEN — API key lacks the read scope, or the key is a sandbox (rw_test) key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"description":"RATE_LIMITED — usage/plan limit exceeded (see error.retry_after)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"500":{"description":"INTERNAL_ERROR","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}}},"/checks/vies":{"post":{"operationId":"checkVies","summary":"One-shot EU VIES VAT number check","description":"Checks an EU VAT number against the European Commission VIES service (live member-state lookup). A member-state outage is a 503 with retry_after, never valid=false. Metered as one API request; refunded on errors. Requires the read scope; rw_test keys get 403.","parameters":[{"name":"Idempotency-Key","in":"header","required":true,"description":"Required for POST/DELETE requests; PATCH does not currently require this header. Printable ASCII, 1-128 characters, not starting with the reserved prefix \"user:\". Reuse only for the exact same method, path and body.","schema":{"type":"string","minLength":1,"maxLength":128,"pattern":"^[ -~]+$"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CheckViesRequest"}}}},"responses":{"200":{"description":"VIES verdict","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/Envelope"},{"type":"object","properties":{"data":{"$ref":"#/components/schemas/CheckViesResponse"}}}]}}}},"400":{"description":"VALIDATION_ERROR — malformed body/params (includes error.details.issues)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"401":{"description":"UNAUTHORIZED — missing, malformed, revoked or expired API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"FORBIDDEN — API key lacks the required scope, plan limit, or source not on plan","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"404":{"description":"NOT_FOUND — entity is not on this organization watchlist","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"409":{"description":"CONFLICT — Idempotency-Key reused, still processing, outcome indeterminate, or one-time secret cannot be replayed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"description":"RATE_LIMITED — usage/plan limit exceeded (see error.retry_after)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"500":{"description":"INTERNAL_ERROR","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"502":{"description":"SOURCE_ERROR — upstream official source returned an error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"503":{"description":"SOURCE_UNAVAILABLE — VIES or a member-state database is unavailable/busy (VIES errors MS_UNAVAILABLE, TIMEOUT, SERVICE_UNAVAILABLE, MS_MAX_CONCURRENT_REQ, GLOBAL_MAX_CONCURRENT_REQ; this is not a validity verdict, retry after error.retry_after), or the sanctions tables are not available yet","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}}},"/checks/sanctions":{"post":{"operationId":"checkSanctions","summary":"One-shot sanctions screening (MSWiA, EU FSF, OFAC SDN)","description":"Screens a Polish NIP and/or a name against locally synced copies of the Polish MSWiA list, the EU consolidated financial sanctions list and the OFAC SDN list. Exact identifier match first, then exact normalized name/alias; no fuzzy scoring. A list older than 48h makes an empty result inconclusive. Metered as one API request. Requires the read scope; rw_test keys get 403.","parameters":[{"name":"Idempotency-Key","in":"header","required":true,"description":"Required for POST/DELETE requests; PATCH does not currently require this header. Printable ASCII, 1-128 characters, not starting with the reserved prefix \"user:\". Reuse only for the exact same method, path and body.","schema":{"type":"string","minLength":1,"maxLength":128,"pattern":"^[ -~]+$"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CheckSanctionsRequest"}}}},"responses":{"200":{"description":"Screening result","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/Envelope"},{"type":"object","properties":{"data":{"$ref":"#/components/schemas/CheckSanctionsResponse"}}}]}}}},"400":{"description":"VALIDATION_ERROR — malformed body/params (includes error.details.issues)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"401":{"description":"UNAUTHORIZED — missing, malformed, revoked or expired API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"FORBIDDEN — API key lacks the required scope, plan limit, or source not on plan","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"404":{"description":"NOT_FOUND — entity is not on this organization watchlist","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"409":{"description":"CONFLICT — Idempotency-Key reused, still processing, outcome indeterminate, or one-time secret cannot be replayed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"description":"RATE_LIMITED — usage/plan limit exceeded (see error.retry_after)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"500":{"description":"INTERNAL_ERROR","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"502":{"description":"SOURCE_ERROR — upstream official source returned an error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"503":{"description":"SOURCE_UNAVAILABLE — VIES or a member-state database is unavailable/busy (VIES errors MS_UNAVAILABLE, TIMEOUT, SERVICE_UNAVAILABLE, MS_MAX_CONCURRENT_REQ, GLOBAL_MAX_CONCURRENT_REQ; this is not a validity verdict, retry after error.retry_after), or the sanctions tables are not available yet","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}}}}}