Developers / SDK
Build document checks into your workflow.
Server integrations and scoped browser sessions.
Six surfaces, one API
.NET
PreviewServer · .NET 8 or .NET 10 (net8.0; net10.0)
Koldera.SdkNode.js
PreviewServer · Node.js 22 or newer (ESM)
@koldera/sdk (entry @koldera/sdk/node)Browser JS
PreviewBrowser · Modern browsers (ESM); same package as Node.js, separate entry
@koldera/sdk (entry @koldera/sdk/browser)React
PreviewBrowser · React 19 with the browser entry of @koldera/sdk
@koldera/sdk-reactPython
PreviewServer · Python 3.11 or newer (sync API over httpx)
accessibility-platform (import accessibility_platform)Go
PreviewServer · Go 1.24 or newer
github.com/makulatura78/koldera-accessibility/sdk/go
Server integration
A predictable request lifecycle
- Create a projectSign in, create or join an organization, then a project. A project has a publishable identifier (
ap_pk_…) and any number of secret keys. - Create a secret keyMade once on the project page and shown once. It lives on your server or in CI secrets — never in a browser, a repository or a
NEXT_PUBLIC_variable. - InstallPick your language below. An install command appears only once the package is verifiably available from the named registry.
- Submit a checkCreate a PDF job with the existing API and wait for its terminal state; a
failedjob is a normal return value, not an exception. - Read the resultProcessing status, the PDF/UA machine check, findings and the manual-review flag are four separate things — see below.
Pick a language
- Package
Koldera.Sdk0.1.0 — NuGet- Credential
- Project secret key (ap_sk_…) from a server environment variable or secrets manager.
- Reference
sdk/dotnet/README.mdin the source repository (access-restricted; not public documentation).
Install
No public install command: this package is not on NuGet yet. Developers with access to the repository can build it from source as described in its README; everyone else can read the example below and wait for the beta announcement on the pricing page.
First check: one PDF, the terminal outcome, findings and manual review
Preview — requires beta package access
using Koldera.Sdk;
using var client = new AccessibilityClient(new AccessibilityClientOptions
{
SecretKey = Environment.GetEnvironmentVariable("KOLDERA_SECRET_KEY"), // server-side only
BaseUrl = new Uri(Environment.GetEnvironmentVariable("KOLDERA_API_URL")!),
});
await using var pdf = File.OpenRead("sample.pdf");
var job = await client.Pdf.CreateJobAsync(pdf, PdfJobModes.Analyze, fileName: "sample.pdf", cancellationToken: ct);
var done = await client.Jobs.WaitForCompletionAsync(job.Id, new WaitOptions { Timeout = TimeSpan.FromMinutes(5) }, ct);
if (done.State == JobStates.Failed) // a failed job is returned, not thrown
throw new InvalidOperationException($"{done.Error!.Code}: {done.Error.Message}");
// "completed" means processing finished; the machine gate is a separate value.
Console.WriteLine($"PDF/UA machine check: {done.Validation?.Status}"); // passed | failed | error | not_run
Console.WriteLine($"Manual review required: {done.ManualReviewRequired}"); // always true
foreach (var finding in await client.Jobs.ListFindingsAsync(job.Id, ct))
Console.WriteLine($"{finding.RuleId}: {finding.Message}");
foreach (var artifact in await client.Jobs.ListArtifactsAsync(job.Id, ct))
{
await using var file = File.Create(artifact.Kind + ".bin");
await client.Artifacts.DownloadAsync(artifact, file, ct); // SHA-256 verified
}completed only says processing finished. Read validation.status for the PDF/UA machine check (passed, failed, error or not_run), the findings for what was verified, and remember that manualReviewRequired stays true: no result is a PDF/UA or WCAG conformance statement.
Also available on this surface
HtmlToPdf.CreateJobAsyncWeb.CreateScanAsyncJobs.GetReadingOrderAsyncJobs.RemediateReadingOrderAsyncClientSessions.CreateAsync
Server versus browser credentials
Your backend holds a project secret key (ap_sk_…) and can create jobs directly. A user’s browser never holds that key — it only ever gets a short-lived session your backend mints for it. This website’s own free tools are a separate, keyless anonymous surface, not a keyless SDK tier.
Full credentials tableExactly what each surface holds and can do
| Runs in | Holds | Can create jobs? |
|---|---|---|
| Your backend (.NET, Node.js, Python, Go) | Project secret key ap_sk_… | Yes, on its project, within the organization's plan. |
| A user's browser (Browser JS, React) | Publishable identifier ap_pk_… plus a session token | Only with a session your backend minted through POST /v1/client-sessions after authenticating its own user: origin-bound, scoped, job-budgeted, expiring within ten minutes. The identifier alone is refused (401). |
| This website's free tools | A Turnstile-protected anonymous session | Five checks per day per network location on this site only. This is a different surface, not a keyless SDK tier. |
Common answers from the API
Checking whether this deployment queues web page checks…
Status codes and what to do401, 402, 403, 409, 429, 503 — the full reference table
| Status | Meaning | What to do |
|---|---|---|
| 401 Unauthorized | Invalid, revoked or missing secret key; a browser session that expired, was used from another origin or is only a publishable key. | Rotate the key on the project page; mint a fresh session from your backend. |
| 402 Payment required | The organization has no usable entitlement: no credits left, or no active subscription. | The response carries upgradeUrl; plans are on the pricing page. |
| 403 Forbidden | The key or session lacks the scope (for example a session without order:remediate). | Create a key or session with the scope the call needs; never widen a browser session beyond what the page does. |
| 409 Conflict | IDEMPOTENCY_CONFLICT (same key, different body), STALE_READING_ORDER_SNAPSHOT or ARTIFACT_EXPIRED. | Use a fresh idempotency key per distinct request; reload the snapshot and re-approve; download links and files expire after 24 hours. |
| 429 Too many requests | Concurrency, rate or the plan's fair-use daily cap. | Honour Retry-After / resetAt (UTC); the SDKs already retry idempotent reads twice. |
| 503 Service unavailable | A capability is switched off on the deployment: WEB_CHECKS_UNAVAILABLE, HOSTED_CHECKS_UNAVAILABLE. | Nothing was reserved or charged; PDF and HTML jobs are unaffected by a web-check outage. |
Limits and billing
SDK jobs draw on the organization's plan: job credits, or a subscription with a fair-use daily cap per plan. Page, browser-time and concurrency limits come from the plan; the current configured prices and caps are on the pricing page, not in this documentation, so they never go stale here. During the beta an organization may run on an internal allowance instead. A job that fails on our side releases its reservation; a rejected request costs nothing.
What machine validation does not prove
manualReviewRequired is always true: a completed job or a passing machine check is never a PDF/UA or WCAG conformance statement.
What each field actually meansstate, validation.status, manualReviewRequired and the fix boundary
state: completedmeans the job finished processing. It says nothing about the document.validation.statusis the PDF/UA-1 machine check by veraPDF:passed,failed,errorornot_run. A completed job withfailedis the normal case for many real documents — and for every Chromium-generated PDF today, which lacks the metadata the standard requires.- A tagged structure tree and a passing machine check do not show that image descriptions are meaningful, that the reading order matches the meaning, or that the document conforms to PDF/UA or WCAG.
manualReviewRequiredis therefore alwaystrue. - Safe fixes are limited to two metadata rules applied to a copy; reading-order corrections happen only for an explicitly approved, hash-bound snapshot (
acknowledged: true). No SDK derives an order or an alt text on its own.
Questions developers ask
Frequently asked questionsAPI-only access, browser sessions, URL conversion, CI gating, polling, docs, MCP
- Can I call the API without an SDK?
- Yes — the SDKs are thin clients over the versioned HTTP API described in
contracts/openapi.yaml(access-restricted source repository). SendAuthorization: Bearer ap_sk_…, a freshIdempotency-Keyon every creation call, and poll the job. - Can the browser SDK work with just the publishable key?
- No. It identifies the project only; jobs need a session that your backend mints for an authenticated user.
- Can I convert a URL to PDF?
- No. HTML to PDF takes one self-contained static HTML file (scripts are refused, remote assets are not fetched); use
contentSelectorto print only part of the page. - Can I gate a template in CI?
- Yes, with the server SDK. The repository's
examples/ci-gaterenders your template, runs one HTML → PDF job, compares the machine-verifiable findings with a committed baseline and exits0(no new machine finding),1(regression) or2(usage or API error); manual-review findings are listed and never fail the build. It can also write a versioned JSON report, and source file/line locations appear only when your build supplies a verified source map — never guessed from the rendered page. Notifications are polling-based; there are no webhooks. - Does a job keep running if my process dies while waiting?
- Yes. Waiting is client-side polling; the job finishes server-side. Its record and findings can be read back later; download links and files expire after 24 hours.
- Where is the reference documentation?
- In each SDK's README in the source repository, alongside golden contract fixtures. There is no public documentation site yet; this page is the public entry point.
- Can an AI assistant read my jobs directly?
- Yes, read-only, through the hosted MCP connection (beta) — a separate, OAuth-bound surface from the SDKs above, scoped to one project and unable to start or pay for a check.
Server keys stay on your backend. Browser sessions are scoped and expire after 10 minutes.