Skip to content
Seismic Orchestration

POST /org/kyb/submit

Submit a KYB profile for verification. Atomic save+submit: the request body is persisted as the canonical draft and submitted in the same call, so what you POST is provably what is reviewed. Idempotent: calling twice when the org is already submitted returns the existing state (the body is still persisted as the latest draft).

In production, every submission is reviewed by Seismic before verification begins (see Sandbox below for the sandbox behavior). The status reads submitted while that review is open; it moves to under_review (and any hosted link appears on the state) once verification starts. If the review needs more information, the request arrives by email while the status stays submitted. If the review rejects the submission, the status reads rejected with a rejection_reason saying what to fix: correct the profile and submit again, and a fresh review opens. If the review declines the submission, the status reads terminally_rejected with a rejection_reason: the decision is final and further submissions are refused with 409 VerificationDeclined.

The server:

  1. Persists the request-body KybProfile as the canonical draft.
  2. Validates required fields. Returns 422 KybProfileIncomplete if anything is missing or malformed.
  3. Queues the submission for Seismic's review (production). Verification starts once the review clears — immediately at submit in sandbox.
  4. Returns the resulting KybState.

Requires admin or owner role.

Sandbox: Seismic approves the submission automatically: a successful submit reports kyb_status of approved with no manual review, so you can exercise the full onboarding flow end-to-end. Validation still runs first: an incomplete profile fails with the same 422 KybProfileIncomplete as production.

Request body: a complete KybProfile. The body is the source of truth: the server does not merge with a previously-saved draft.

An optional end_user object may sit beside the profile keys, recording where the person submitting accepted the terms of service: ip_address, user_agent and device_fingerprint, as on POST /customers/:id/kyc/submit. The address and user agent are accepted only with an API key; with a dashboard session they are taken from the request itself. See Device fingerprinting.

business.description must be at least 350 characters (violations[] reason: "too_short"); reviewers need real detail to clear an entity.

business.website is optional. Set business.no_website to true (or simply omit the website) when the entity has no public site — then business.marketing_strategy ("How the entity markets themselves to generate business") is required instead and must be at least 300 characters (violations[] reason: "too_short"). Seismic passes the statement on to compliance review up front, so an entity without a website isn't held up by a follow-up information request. Saving a profile that has a website drops business.marketing_strategy, so it never comes back on a later read.

An entity without a website must also give business.website_eta — an ISO-8601 date in the future, or 0000-00-00 if it does not intend to publish one (violations[] reason: "out_of_range" for a date already passed) — and business.social_media_profile, a profile URL for the entity or one of its beneficial owners (violations[] reason: "invalid_format" when malformed). Seismic records the no-website declaration — the ETA, the social media profile, and the marketing strategy — in a certification it passes to compliance review with the submission (the marketing strategy appears only there, not in the supplemental information document), and drops both fields on save when a website is given.

Submit-time requirements that catch out integrations written against older docs:

  • operations.primary_source_of_funds_description is required regardless of the selected historical source of funds, not only when it is other.
  • Every entry in associated_persons[] must have a title, not only control persons.
  • business.target_customers_description is required. Seismic includes it in the supplemental information document it sends to compliance review, under its own lead-in.
  • A Provider Attestation (licensed_vendors_attestation) document is always required, whatever operations.licensed_vendors says. See Documents.
  • A flow_of_funds document is always required, whatever operations.registered_msb says. See Documents.

Narrative length:

From 2026-11-01, business narratives must meet these lengths on every submit; until then only the 350-character floor on business.description applies. Out-of-range values are reported in violations[] with reason: "too_short" or "too_long":

  • business.description: 350 to 1,000 characters.
  • operations.primary_source_of_funds_description: 200 to 1,000 characters.
  • operations.account_purpose_explanation: 200 to 1,000 characters when given.
  • operations.msb_compliance_screening_description: at least 200 characters when operations.registered_msb or operations.in_flow_of_funds is true.
  • The screening narrative assembled from operations.msb_compliance_screening_description and operations.high_risk_activities_description (or operations.compliance_screening_explanation supplied directly): at most 1,000 characters, section headers included.

From 2026-11-01, operations.registered_msb must be answered true or false; a profile that omits it is reported in missing[] instead of being read as false. When it or operations.in_flow_of_funds is true, operations.registered_msb_description and operations.msb_compliance_screening_description are required. Profiles already verified do not need to be resubmitted.

From 2026-11-14, operations.account_purpose_explanation is required whatever operations.account_purpose is, not only for other; a profile without one is reported in missing[]. Profiles already verified do not need to be resubmitted.

Response 200: current KybState:

{
  "kyb_status": "submitted",
  "kyb_link": null,
  "missing_fields": [],
  "last_synced_at": "2026-04-23T11:00:00Z",
  "rejection_reason": null,
  "pending_rfi": false,
  "last_submitted_at": "2026-04-23T11:00:00Z",
  "resubmit_available": false
}

kyb_link is null while the submission is under review; once verification starts, re-read GET /org/kyb/state for the link.

Errors:

  • 403: not an admin/owner.

  • 409 ResubmitUnchanged: the profile has not changed since the submission that was returned for corrections. Edit the profile or upload a document before resubmitting.

  • 422 KybProfileIncomplete: required fields are missing or malformed on the submitted profile. The missing[] array carries dotted paths for absent fields so the wizard can highlight what the user still needs to fill in. The violations[] array carries fields that are present but fail a format check Seismic runs before dispatch (e.g. a postal code that doesn't match its country's national format). Each entry names the field, a stable reason slug, and a human description with the expected shape. violations[] is omitted when empty.

    {
      "ok": false,
      "error": {
        "code": "KybProfileIncomplete",
        "message": "5 required fields are missing",
        "missing": [
          "business.formation_date",
          "operations.regulated_status",
          "associated_persons[0].tax_id",
          "volumes.usd_value_of_fiat"
        ],
        "violations": [
          {
            "field": "addresses.registered.postal_code",
            "reason": "invalid_format",
            "description": "Invalid postal code format. Must match pattern: ^VG\\d{4}$"
          }
        ]
      }
    }

Example

curl -X POST "$SEISMIC_ORCHESTRATION_URL/v0/org/kyb/submit" \
  -H "Api-Key: $SEISMIC_ORCHESTRATION_API_KEY" \
  -H "Content-Type: application/json" \
  -d @kyb-profile.json