Kita

SDK reference

In-app iOS and Android hosts, web embedding, server endpoints, and troubleshooting.

Web SDK

Load the script from sdk_url, then call KitaRisk.create(options).open(). For CSP, allow your Kita host in script-src, style-src, and frame-src.

OptionTypeRequiredDescription
sessionIdstringyessession_id from create session.
clientTokenstringyesclient_token from the same response. Expires after 30 minutes.
containerelement or selectornoRender inline inside this element. Omit for a modal dialog.
connectUrlstringnoconnect_url from the session response. Needed only when the script is served from a different host than the flow.
onSubmittedfunctionno({ sessionId, status: 'accepted' }). The borrower sent their files.
onExitfunctionno({ sessionId }). Closed before finishing; uploads are kept until the session expires.
onErrorfunctionno({ code, sessionId }). See errors; most are recoverable in the flow.
onEventfunctionno({ name, sessionId }): consent_recorded, documents_added, documents_submitted.

Methods: open(), back(), exit(), and destroy() (call on unmount). An inline container needs at least 360 × 640 CSS pixels. To resume, create a new handle with the same session ID and a new client token. Verify outcomes from your server, not browser callbacks.

Server API

Send X-API-Key with your Risk Score key to the same base URL as the Scoring API: https://riskscore.kita.ai for kita_prod_ keys, https://sandbox-api.kita.ai for kita_sbx_ keys. Your organization path (/<org>/v1/risk/...) works too. Manage keys on the dashboard's API keys page, and allowed origins and your logo on its SDK page, which also lists recent runs. Never send an API key to a browser or mobile app.

MethodPathResult
POST/v1/risk/sessionsCreate a session; returns 201 with identifiers, token, expiry, SDK URL, and connect URL.
POST/v1/risk/sessions/{id}/client-tokenIssue a new 30-minute token; the previous token stops working.
GET/v1/risk/sessions/{id}Read status and per-file outcomes.
GET/v1/risk/sessions?offset=0List sessions, 40 per page, newest first.
GET/v1/risk/sessions/{id}/resultsRead results; returns 409 results_not_ready before a terminal state.
DELETE/v1/risk/sessions/{id}Delete session, files, and results; returns 204.

Create a session

FieldTypeRequiredDescription
reference_idstring, 1–128yesYour applicant or loan ID.
originstringyesThe exact origin that embeds the SDK, saved as an allowed origin on the dashboard's SDK page. For iOS and Android, the app origin you pass to the SDK: an HTTPS origin you own; nothing has to be served there.
display_namestring, 1–60noOverrides the name borrowers see for this session. Default: your organization's name, shown beside your logo.
countryMX or PHnoDefault MX. Must be a country enabled for you.
locationoptional or disablednoLegacy; use disabled.

Send an Idempotency-Key header (up to 128 characters) per applicant. Repeating the same key and body returns the same session with a new client token; a different body returns 409 idempotency_conflict.

Returns 201 with session_id, client_token, expires_at, sdk_url, connect_url and environment.

Session status

collecting → queued → processing → completed, partial, or failed. Check each document's status and reason. Scores are included only with live scoring.

Python helper

Download kita_risk.py (standard library only).

import os
from kita_risk import KitaRisk
 
kita = KitaRisk(api_key=os.environ["KITA_API_KEY"], base_url=os.environ["KITA_BASE_URL"])
session = kita.create_session(reference_id="LOAN-2026-001", origin="https://app.example.com")
status = kita.status(session["session_id"])
# When the borrower returns:
token = kita.client_token(session["session_id"])

Webhooks

Kita turns on webhooks for your organization on request: send the HTTPS URL and we will share the signing secret. Events carry id, session_id, and a type: risk.session.completed, risk.session.partial, or risk.session.failed. They contain no scores; fetch results.

Verify Kita-Signature: t=TIMESTAMP,v1=DIGEST, where the digest is HMAC-SHA256 of TIMESTAMP.RAW_BODY with your webhook secret (or use KitaRisk.verify_webhook(secret, signature_header, raw_body)). Reject timestamps older than five minutes and deduplicate event IDs. Return 2xx after storing; failures retry up to six times.

iOS

Add the supplied KitaRisk Swift package and present KitaRiskViewController (iOS 15+). It runs in-app with the photo, Files and camera pickers.

import KitaRisk
 
let session = try KitaSession(
    id: response.sessionID,
    clientToken: response.clientToken,
    sdkOrigin: URL(string: "https://riskscore.kita.ai")!, // sandbox: https://sandbox-api.kita.ai
    appOrigin: URL(string: "https://app.example.com")!
)
let controller = KitaRiskViewController(session: session) { event in
    switch event {
    case .submitted, .exited:
        host.dismiss(animated: true)
    case .failed:
        // Offer retry with a fresh token from your backend.
        break
    }
}
host.present(controller, animated: true)

Add NSCameraUsageDescription to Info.plist. Both origins must be HTTPS, and appOrigin must match the session and an allowed origin. The callback confirms receipt only; fetch results on your server.

Android

Android has no drop-in screen yet: host the flow in your own WebView with the supplied capture Gradle module (Android 8+, Java 17, compile SDK 36).

  1. Enable JavaScript and DOM storage, and allow navigation only to the SDK origin and your app origin.
  2. Attach the bridge, then load a small host page under your app origin that loads {sdkOrigin}/sdk/v1/kita-risk.js and kita-risk.css and calls KitaRisk.create({ sessionId, clientToken, baseUrl: sdkOrigin, container: '#sdk' }).open().
  3. Handle WebChromeClient.onShowFileChooser for photos and documents, and receive onSubmitted / onExit / onError through a message listener scoped to your app origin (WebViewCompat.addWebMessageListener).
String sdkOrigin = "https://riskscore.kita.ai"; // sandbox: https://sandbox-api.kita.ai
CaptureConfig config = new CaptureConfig(sdkOrigin, sessionId, clientToken, 702);
KitaCapture capture = new KitaCapture(activity, webView, config);
webView.loadDataWithBaseURL(appOrigin + "/", hostHtml, "text/html", "utf-8", null);
// Never put the server API key in the app.

Forward onActivityResult and onRequestPermissionsResult to the bridge, and call capture.close() when the host is destroyed. Call capture.setPhotoCaptureAvailable(true) only if your file chooser supports the camera. The SDK repository's Android sample is a complete host to start from.

Testing uploads

Use the SDK repository's iOS and Android samples and Maestro journeys; see docs/SDK_MOBILE_TESTING.md.

Errors

Errors contain error.code and error.request_id; include the request ID when contacting Kita.

StatusCodeFix
401invalid_api_keyMissing, truncated, rotated, or revoked key. A key used on the other environment's base URL gets wrong_environment, which names the right URL.
403sdk_not_enabledYour organization has no allowed origins for this environment yet. Add them on the dashboard's SDK page.
403origin_not_allowedorigin must exactly match one of your allowed origins: scheme, host and port, no path.
403profile_not_enabledThis country is not enabled for your organization.
404session_not_foundWrong ID, another organization's session, or expired.
409idempotency_conflictSame Idempotency-Key, different body. Use one key per applicant.
409results_not_readyWait for the webhook or for a terminal status.
413request_too_largeOver the size limit.
422invalid_request, market_profile_not_validatedA field failed validation (error.fields names it), or country does not match your profile. Checked before the key.
429rate_limitedWait for Retry-After seconds.
503sdk_settings_unavailableYour SDK settings could not be read. Retry after Retry-After seconds.

Upload errors

CodeCauseWhat happens
encrypted_pdfThe PDF needs a document password.The flow asks for it.
invalid_pdf_passwordWrong document password.The borrower can retry.
unsupported_formatNot a PDF or supported image.The borrower picks another file.
invalid_image, corrupt_pdf, empty_documentThe file cannot be opened.The borrower picks another file.
document_too_large, too_many_pages, oversized_imageOver a limit.The borrower picks a smaller copy.
multiple_image_framesAn animated or multi-frame image.Export pages as a PDF or separate photos.
document_limit_reached12 files already uploaded.Remove one first.
connection_error, rate_limitedNetwork trouble or throttling.The flow offers Retry.
client_token_expired, invalid_client_token, missing_client_tokenThe 30-minute token ran out, was replaced, or was not sent.Get a new client token and reopen.
session_lockedThe files were already shared.Nothing to do; the session shows its receipt.

Finished sessions may report per-file model_rejected, blank_document, extraction_error, or processing_timeout. These are analysis outcomes; the borrower still sees a receipt.

Limits

Write requests120 per minute per organization, in bursts of up to 30. 429 carries Retry-After.
File size100 MB per file uploaded through the SDK. The Scoring API accepts up to 30 MB per document.
Files per session12
PDF pages150 per file
Client token lifetime30 minutes
Session retention24 hours unless agreed otherwise

On this page