---
title: "Agent-first Swiss business verification (one call)"
method: POST
path: "/api/v1/verify-business"
---

# Agent-first Swiss business verification (one call)

`POST /api/v1/verify-business`

## Request

- Base URL: `https://bumit.ai`
- URL: `https://bumit.ai/api/v1/verify-business`
- Auth: none required

Primary BUMIT operation. No authentication, no API key, no account; no payment is currently required. Request bodies are not logged and no caller identity is stored.

Send `Content-Type: application/json` (otherwise 415). Maximum body 8192 bytes (otherwise 413). Body: any supported combination of uid, name and address; only these fields are accepted (unknown fields → 400). A UID must carry the CHE prefix and a valid mod-11 check digit, otherwise the request is rejected (422) before any source lookup.

Resolution: a UID resolves cache-first against the commercial register. A name alone is enough to start: BUMIT searches the commercial register (discovery), then resolves identity conservatively. Exactly one exact normalized legal-name match resolves its UID and runs the same verification as a UID request. Several exact matches (optionally narrowed by address.locality = seat) return AMBIGUOUS / MULTIPLE_EXACT_NAME_MATCHES. No exact match but register candidates (even one) return AMBIGUOUS / NAME_CANDIDATES_REQUIRE_SELECTION — partial or similar names are never auto-selected, and locality never narrows them. No candidates → NOT_FOUND. A partial local cache never proves absence, uniqueness or identity (UNAVAILABLE / REGISTER_SEARCH_UNAVAILABLE). To continue after AMBIGUOUS, choose a candidate and call again with {"uid": "<candidate uid>"}. Address alone is UNSUPPORTED for resolution.

Overall outcome is derived deterministically from REQUIRED checks (register existence, register status, direct SECO screening against the accepted production baseline, and name match when uid and name are both supplied): any NOT_VERIFIED → NOT_VERIFIED; else any REVIEW_REQUIRED → REVIEW_REQUIRED; else any other non-VERIFIED → UNAVAILABLE; else VERIFIED. An unresolved business returns the resolution outcome (NOT_FOUND, AMBIGUOUS, UNAVAILABLE, UNSUPPORTED). FINMA authorisation-list membership is reported but never required. VAT, address, LEI and FINMA warning checks have no production source and are reported as UNSUPPORTED (NOT_REQUESTED when the input was not supplied). See `x-agent-guidance`.

Interpreting the evidence: read `result.outcome` first, then each `checks[]` entry. `sources` names the source (ZEFIX = commercial-register index; SECO_SANCTIONS and FINMA = operator-ingested datasets), `data_basis` says whether it is a cache-first register lookup, an accepted production dataset, a synthetic fixture or NONE, `provenance` carries the source record or dataset reference, and `freshness` carries the times BUMIT actually holds (`observed_at`, `retrieved_at`, `source_timestamp`) plus `served_from_cache` and `stale`. A null timestamp is unknown; there is no universal last-updated time.

SECO and FINMA are never contacted by this operation, and it cannot trigger any acquisition. Responses are `Cache-Control: no-store`. Not a complete KYB result.

## Request body

- object
  - `uid` string: Swiss UID, canonical (CHE-123.456.789) or compact (CHE123456789). Check digit validated server-side.
  - `name` string: Exact legal name. Name-only resolution needs at least 3 characters. Exact normalized equality only; never fuzzy.
  - `address` object
    - `street` string, required
    - `house_number` string, required
    - `postcode` string, required
    - `locality` string, required

## Response `200`

Structured verification result. Every outcome, including NOT_FOUND / AMBIGUOUS / UNAVAILABLE / UNSUPPORTED, is a 200.

- BusinessVerificationResponse
  - `status` 'OK', required
  - `contract` object, required
    - `id` 'BUMIT_BUSINESS_VERIFICATION', required
    - `version` '1.0.0-phase13.2', required
    - `ruleset_version` string, required
    - `profile_id` string, required
    - `profile_version` string, required
    - `evaluation_context` 'PRODUCTION' | 'FIXTURE_TEST', required
    - `outcome_vocabulary` string[], required
    - `resolution_vocabulary` string[], required
  - `request` object, required
    - `supplied` object, required
      - `uid` boolean, required
      - `name` boolean, required
      - `address` boolean, required
    - `normalized_uid` string, nullable, required
  - `resolution` BusinessResolution, required
    - `outcome` 'RESOLVED' | 'NOT_FOUND' | 'AMBIGUOUS' | 'UNAVAILABLE' | 'UNSUPPORTED', required
    - `method` 'UID' | 'EXACT_NAME' | 'EXACT_NAME_AND_LOCALITY' | 'null', nullable, required
    - `reason_code` 'RESOLVED_BY_UID' | 'RESOLVED_BY_UNIQUE_EXACT_NAME' | 'SOURCE_NOT_CONFIGURED' | 'SOURCE_UNAVAILABLE' | 'COMPANY_NOT_FOUND' | 'REGISTER_SEARCH_UNAVAILABLE' | 'NO_EXACT_NAME_MATCH' | 'MULTIPLE_EXACT_NAME_MATCHES' | 'NAME_CANDIDATES_REQUIRE_SELECTION' | 'CANDIDATE_UID_UNUSABLE' | 'ADDRESS_ONLY_RESOLUTION_NOT_SUPPORTED', required
    - `business` object, nullable, required
      - `uid` string, nullable, required
      - `legal_name` string, required
      - `legal_form` string, nullable, required
      - `legal_status` string, nullable, required
      - `seat` string, nullable, required
      - `canton` string, nullable, required
    - `candidate_count` integer, required: Number of entries in candidates (after the response bound).
    - `candidates_truncated` boolean, required: True when the register search returned more distinct candidates than this response carries. Candidates are never claimed to be exhaustive.
    - `candidates` object[], required: AMBIGUOUS company candidates from the commercial-register search, deduplicated by UID and ordered deterministically (uid, legal_name, seat, canton). Discovery only — never auto-selected and not verified; resubmit with a selected uid.
      - `uid` string, nullable, required
      - `legal_name` string, required
      - `seat` string, nullable, required
      - `canton` string, nullable, required
  - `result` object, required
    - `outcome` 'VERIFIED' | 'NOT_VERIFIED' | 'REVIEW_REQUIRED' | 'UNAVAILABLE' | 'NOT_FOUND' | 'AMBIGUOUS' | 'UNSUPPORTED', required
    - `reason_code` 'SOURCE_NOT_CONFIGURED' | 'SOURCE_UNAVAILABLE' | 'COMPANY_NOT_FOUND' | 'REGISTER_SEARCH_UNAVAILABLE' | 'NO_EXACT_NAME_MATCH' | 'MULTIPLE_EXACT_NAME_MATCHES' | 'NAME_CANDIDATES_REQUIRE_SELECTION' | 'CANDIDATE_UID_UNUSABLE' | 'ADDRESS_ONLY_RESOLUTION_NOT_SUPPORTED' | 'REQUIRED_CHECK_NOT_VERIFIED' | 'REQUIRED_CHECK_REVIEW' | 'REQUIRED_CHECK_UNAVAILABLE' | 'ALL_REQUIRED_CHECKS_VERIFIED', required
    - `required_capabilities` string[], required
    - `blocking_capabilities` string[], required
    - `derivation` 'DETERMINISTIC_REQUIRED_CHECKS', required
    - `weighted_score_used` unknown, required
    - `probabilistic_decision_used` unknown, required
    - `fuzzy_matching_used` unknown, required
    - `llm_used` unknown, required
  - `checks` BusinessCheck[], required
    - `capability` 'REGISTRY_EXISTENCE' | 'REGISTER_STATUS' | 'NAME_MATCH' | 'SANCTIONS_CHECK' | 'FINMA_AUTHORISATION_CHECK' | 'FINMA_WARNING_CHECK' | 'VAT_VERIFY' | 'ADDRESS_VERIFY' | 'LEI_LOOKUP', required
    - `requirement` 'REQUIRED' | 'NOT_REQUIRED', required: Only REQUIRED checks determine result.outcome.
    - `outcome` 'VERIFIED' | 'NOT_VERIFIED' | 'NOT_FOUND' | 'AMBIGUOUS' | 'REVIEW_REQUIRED' | 'UNAVAILABLE' | 'UNSUPPORTED' | 'NOT_REQUESTED', required
    - `reason_code` string, required: Stable machine code from the underlying layer (see components.schemas.ReasonCode and the layer-specific codes documented on each capability).
    - `source_state` string, nullable, required
    - `source_reason_codes` string[], required
    - `sources` string[], required
    - `data_basis` 'LIVE_SOURCE_CACHE_FIRST' | 'PRODUCTION_ACCEPTED_BASELINE' | 'SYNTHETIC_FIXTURE' | 'NONE', required
    - `provenance` object, nullable, required
    - `freshness` Freshness, required: Cached evidence is never reported as LIVE.
      - `observed_at` string, date-time, nullable, required
      - `retrieved_at` string, date-time, nullable: Set only when this response came from a successful upstream call.
      - `source_timestamp` string, nullable
      - `age_seconds` integer, nullable
      - `max_age_hours` integer, nullable
      - `stale` boolean, required
      - `served_from_cache` boolean, required
      - `source_state` 'LIVE' | 'CACHED' | 'STALE' | 'DEGRADED' | 'UNAVAILABLE', required
      - `upstream_status` 'AVAILABLE' | 'NOT_CHECKED' | 'DEGRADED' | 'UNAVAILABLE' | 'NOT_CONFIGURED', required: Current upstream condition. NOT_CHECKED means this request was served cache-first and never contacted the source, so no availability claim is made; it is not a failure. Pair it with upstream_checked: false and source_state: CACHED.
      - `reason_code` 'INVALID_UID' | 'COMPANY_NOT_FOUND' | 'SOURCE_NOT_CONFIGURED' | 'SOURCE_UNAVAILABLE' | 'SOURCE_TIMEOUT' | 'SOURCE_RATE_LIMITED' | 'STALE_EVIDENCE' | 'INSUFFICIENT_EVIDENCE' | 'CAPABILITY_NOT_SUPPORTED' | 'CAPABILITY_NOT_AVAILABLE' | 'AUTHENTICATION_REQUIRED' | 'ACCESS_DENIED' | 'RATE_LIMITED' | 'UPSTREAM_DEGRADED' | 'INTERNAL_ERROR' | 'BAD_REQUEST' | 'PARTIAL_COVERAGE' | 'SANCTIONS_NO_DIRECT_MATCH' | 'SANCTIONS_POSSIBLE_MATCH' | 'SANCTIONS_CONFIRMED_MATCH' | 'SANCTIONS_DATA_UNAVAILABLE' | 'SANCTIONS_DATA_STALE' | 'SANCTIONS_SCHEMA_UNSUPPORTED' | 'SANCTIONS_CHECK_INCOMPLETE' | 'FINMA_LIST_UID_MATCH' | 'FINMA_LIST_UID_NOT_FOUND' | 'FINMA_LIST_UID_UNUSABLE' | 'FINMA_AUTHORISATION_DATA_UNAVAILABLE' | 'FINMA_AUTHORISATION_DATA_STALE' | 'FINMA_AUTHORISATION_DATA_INVALID' | 'FINMA_AUTHORISATION_CHECK_INCOMPLETE' | 'FINMA_WARNING_NO_DIRECT_MATCH' | 'FINMA_WARNING_POSSIBLE_MATCH' | 'FINMA_WARNING_DATA_UNAVAILABLE' | 'FINMA_WARNING_DATA_STALE' | 'FINMA_WARNING_DATA_INVALID' | 'FINMA_WARNING_CHECK_INCOMPLETE' | 'LEI_RECORD_FOUND' | 'LEI_RECORD_NOT_FOUND' | 'LEI_MULTIPLE_RECORDS' | 'LEI_LINKAGE_IDENTIFIER_UNUSABLE' | 'LEI_DATA_UNAVAILABLE' | 'LEI_DATA_INVALID' | 'LEI_DATA_STALE' | 'LEI_LOOKUP_INCOMPLETE' | 'VAT_ACTIVE_REGISTRATION' | 'VAT_INACTIVE_REGISTRATION' | 'VAT_NO_REGISTRATION' | 'VAT_STATUS_UNRECOGNISED' | 'VAT_EVIDENCE_CONTRADICTORY' | 'VAT_RECORD_NOT_FOUND' | 'VAT_MULTIPLE_SOURCE_RECORDS' | 'VAT_SUBJECT_UID_UNUSABLE' | 'VAT_DATA_UNAVAILABLE' | 'VAT_DATA_INVALID' | 'VAT_DATA_STALE' | 'VAT_CHECK_INCOMPLETE' | 'ADDRESS_OFFICIAL_MATCH' | 'ADDRESS_PLANNED_MATCH' | 'ADDRESS_OUTDATED_MATCH' | 'ADDRESS_NOT_FOUND' | 'ADDRESS_MULTIPLE_MATCHES' | 'ADDRESS_QUERY_UNUSABLE' | 'ADDRESS_STATUS_MISSING' | 'ADDRESS_STATUS_UNRECOGNISED' | 'ADDRESS_OFFICIAL_FLAG_NOT_SET' | 'ADDRESS_EVIDENCE_CONTRADICTORY' | 'ADDRESS_DATA_UNAVAILABLE' | 'ADDRESS_DATA_INVALID' | 'ADDRESS_DATA_STALE' | 'ADDRESS_CHECK_INCOMPLETE' | 'MANDATORY_CHECK_FAILED' | 'MANDATORY_CHECK_REVIEW' | 'MANDATORY_CHECK_UNKNOWN' | 'VERIFICATION_PROFILE_SATISFIED': Stable machine-readable reason identifier.
    - `semantic_boundary` string, required
  - `coverage` VerificationCoverage, required: Deterministic projection of `checks` (canonical check order preserved); `checks` remains authoritative. `performed` lists capabilities BUMIT actually evaluated — this does NOT mean the result was favourable (NOT_VERIFIED, NOT_FOUND or REVIEW_REQUIRED are performed checks). `not_performed` lists capabilities that were UNSUPPORTED, UNAVAILABLE or NOT_REQUESTED; each entry keeps its actual outcome and reason_code. Empty lists when the business was not resolved.
    - `derivation` 'PROJECTION_OF_CHECKS', required
    - `performed` CoverageEntry[], required
      - `capability` 'REGISTRY_EXISTENCE' | 'REGISTER_STATUS' | 'NAME_MATCH' | 'SANCTIONS_CHECK' | 'FINMA_AUTHORISATION_CHECK' | 'FINMA_WARNING_CHECK' | 'VAT_VERIFY' | 'ADDRESS_VERIFY' | 'LEI_LOOKUP', required
      - `requirement` 'REQUIRED' | 'NOT_REQUIRED', required
      - `outcome` 'VERIFIED' | 'NOT_VERIFIED' | 'NOT_FOUND' | 'AMBIGUOUS' | 'REVIEW_REQUIRED' | 'UNAVAILABLE' | 'UNSUPPORTED' | 'NOT_REQUESTED', required
      - `reason_code` string, required
    - `not_performed` CoverageEntry[], required
      - `capability` 'REGISTRY_EXISTENCE' | 'REGISTER_STATUS' | 'NAME_MATCH' | 'SANCTIONS_CHECK' | 'FINMA_AUTHORISATION_CHECK' | 'FINMA_WARNING_CHECK' | 'VAT_VERIFY' | 'ADDRESS_VERIFY' | 'LEI_LOOKUP', required
      - `requirement` 'REQUIRED' | 'NOT_REQUIRED', required
      - `outcome` 'VERIFIED' | 'NOT_VERIFIED' | 'NOT_FOUND' | 'AMBIGUOUS' | 'REVIEW_REQUIRED' | 'UNAVAILABLE' | 'UNSUPPORTED' | 'NOT_REQUESTED', required
      - `reason_code` string, required
  - `freshness` object, required: Keyed by capability.
  - `warnings` object[], required
    - `code` string, required
    - `message` string, required
  - `pass_semantics` object, required
  - `caller_identity_used` unknown, required
  - `persisted_request_data` unknown, required
  - `disclaimers` object, required
    - `zefix` unknown, required
    - `seco` unknown, required
    - `finma` unknown, required
  - `meta` Meta, required
    - `service` string, required
    - `api_version` string, required
    - `trace_id` string, uuid, required
    - `generated_at` string, date-time, required

## Other responses

- `400`: Malformed JSON, empty body, non-object body, unknown field, field too long or invalid field (BAD_REQUEST; `error.details.reason` names the rule).
- `405`: Method other than POST (METHOD_NOT_ALLOWED).
- `413`: Body larger than 8192 bytes (PAYLOAD_TOO_LARGE); no source was contacted, no usage event.
- `415`: Content-Type is not application/json (UNSUPPORTED_MEDIA_TYPE); no source was contacted, no usage event.
- `422`: Structurally invalid UID or invalid check digit (INVALID_UID); no source was contacted.
- `500`: Unexpected internal error (INTERNAL_ERROR). No internal detail is exposed.

---

[API](https://skmtc.dev/bumit/apis/bumit-api.md) · [All operations](https://skmtc.dev/bumit/apis/bumit-api/llms.txt) · [OpenAPI document](https://skmtc.dev/bumit/apis/bumit-api/revisions/913e5d3654f8?raw)
