Sertainly Marketplace

Buyer docs

Everything you need to call a suite you have subscribed to — over plain HTTP or from an AI coding agent over MCP. Same evaluation, same metering, either way. The listing page of each package has a curl command pre-filled with that package's sample case; this page explains the rest.

1. Get a key

Subscribe to a suite, then mint a key from your dashboard — one is minted for you automatically the first time you subscribe. Keys start with sert_mk_ and are shown once: copy the key when it appears; it cannot be retrieved later. If you lose it, revoke it and mint another.

A key belongs to one subscription, and therefore one suite. It can call every package in that suite and nothing else. Send it as a bearer token on every request:

Authorization header
Authorization: Bearer sert_mk_YOUR_KEY

Keys on a suspended or cancelled subscription stop working immediately (403, see errors below) and start working again if the subscription is reactivated — nothing needs re-minting.

2. Call over HTTP

Base URL: https://api.sertainly.ai/api/marketplace/v1. All endpoints return JSON. Package slugs go in the URL; there is no version parameter — a subscription always evaluates against the publisher's current production release, and the response tells you which version ran.

List the packages in your suite

Reads do not count toward your quota.

GET /api/marketplace/v1/packages
curl https://api.sertainly.ai/api/marketplace/v1/packages \
  -H "Authorization: Bearer sert_mk_YOUR_KEY"

Returns { suite: { slug, title, tagline, category, jurisdiction }, packages: [ { slug, title, tagline, description, marketing_overview, sample_case, contract_major, status, deprecation? } ], total }. A package whose status is deprecated carries a deprecation object (notice, sunset_at) and keeps working until the sunset date.

Get a package's input contract

GET /api/marketplace/v1/packages/{slug}/schema
curl https://api.sertainly.ai/api/marketplace/v1/packages/PACKAGE_SLUG/schema \
  -H "Authorization: Bearer sert_mk_YOUR_KEY"

Returns { slug, version, fields: [ { path, type, required?, semantic_type?, description?, enum? } ] }. Fields are flat, dotted paths (employee.state). type is string / number / integer / boolean; semantic_type (date, datetime, currency, percentage) is a hint for collecting and formatting the value. The same contract is shown as a table on each listing page.

Evaluate a case

Counts toward your quota. The body is a single case object whose keys are the contract's field paths. Every listing page has this command pre-filled with the publisher's sample case under Integrate — start there.

POST /api/marketplace/v1/packages/{slug}/evaluate
curl -X POST https://api.sertainly.ai/api/marketplace/v1/packages/PACKAGE_SLUG/evaluate \
  -H "Authorization: Bearer sert_mk_YOUR_KEY" \
  -H "content-type: application/json" \
  -d '{
  "case": {
    "field.path": "value"
  }
}'

3. Read the response

The result is channel-shaped: instead of one verdict, four independent signals plus a one-word status rollup for quick branching. The same evaluation, run twice, returns the same answer — there is no model in the loop.

200 OK
{
  "trace_id": "trc_01J7M3...",
  "bdl_version": 3,
  "status": "ok",
  "package_version": 4,
  "evaluated_at": "2026-08-28T12:00:00Z",
  "case_validity": { "state": "valid" },
  "execution": { "state": "succeeded" },
  "outputs": {
    "tags": ["standard_rate"],
    "routes": [{ "to": "file_return" }],
    "evidence_needed": [],
    "reason_codes": ["THRESHOLD_MET"],
    "facts": { "rate_applied": 0.2 }
  },
  "applied_rules": ["DEF_RATE", "APPLY_THRESHOLD"],
  "skipped_rules": ["EXEMPTION_SMALL_BUSINESS"]
}
  • statusok | invalid | halted | failed, derived from the channels below.
  • case_validity — was the input structurally usable? When invalid, missing_fields and/or errors say why.
  • execution — did the engine finish? halted_by names the rule that stopped it.
  • outputstags, routes (where the case goes next), evidence_needed (what the policy needs before it can decide), reason_codes, derived facts. Any may be absent.
  • applied_rules / skipped_rules — the rule ids that fired versus those whose conditions did not match.
  • package_version — the publisher version that actually ran; trace_id — see Traces.
  • deprecation — present only when the package is deprecated: the publisher's notice and sunset_at.

Invalid input is a 200, not a 4xx. A case that is missing fields or has the wrong types comes back as a normal result with status: "invalid" — read case_validity, fix the case, and try again. It still counts as one call.

200 OK — structurally invalid case
{
  "trace_id": "trc_01J7M3...",
  "bdl_version": 3,
  "status": "invalid",
  "package_version": 4,
  "evaluated_at": "2026-08-28T12:00:00Z",
  "case_validity": { "state": "invalid", "missing_fields": ["employee.state"] },
  "execution": { "state": "halted", "halted_by": "structural_input_validation" },
  "outputs": {},
  "applied_rules": [],
  "skipped_rules": []
}

4. Errors, quota, rate limits

Errors use one flat envelope: { "error": "<message>", "code": "<CODE>" }. Codes are stable; messages may change.

429
{
  "error": "Subscription call quota reached.",
  "code": "QUOTA_EXCEEDED"
}
HTTPCodeMeaning
400VALIDATION_FAILEDBody is not JSON, or has no `case` object.
401UNAUTHORIZEDMissing, malformed, or revoked `sert_mk_` key.
403CONSUMER_SUSPENDEDYour marketplace account is suspended.
403ENTITLEMENT_SUSPENDEDThis subscription is suspended (for example, payment failed).
403ENTITLEMENT_REVOKEDThis subscription is no longer active.
404SUITE_NOT_AVAILABLEThe suite behind this key is not listed on the marketplace.
404PACKAGE_NOT_FOUNDNo such package in your suite (or it has been delisted).
404TRACE_NOT_FOUNDThe trace id is not one of your subscription's traces.
410LISTING_SUNSETThe package was deprecated and its sunset date has passed.
429RATE_LIMITEDToo many requests — retry after `Retry-After` seconds.
429QUOTA_EXCEEDEDThe subscription's call quota for this period is used up.
500INTERNAL_ERRORThe package could not be evaluated — contact the publisher.
503LISTING_UNAVAILABLEThe package's published version is temporarily unreadable.

Quota

Each subscription has a call allowance per billing period; only evaluate calls count. Every evaluate response — success or 429 — carries X-Sertainly-Quota-Used, and X-Sertainly-Quota-Limit when the subscription is capped, so you can monitor consumption without waiting for an error. When the cap is reached you get 429 QUOTA_EXCEEDED until the period renews. Usage is also shown on your dashboard.

Rate limit

Separately from quota, each subscription may make up to 500 requests per second and 25,000 per minute across all its keys (this applies to the package and schema reads as well as evaluate). Over that you get 429 RATE_LIMITED with Retry-After (seconds) and X-RateLimit-Limit / X-RateLimit-Remaining / X-RateLimit-Reset headers.

Deprecation

When a publisher deprecates a package, its responses carry X-Sertainly-Deprecated: true, X-Sertainly-Sunset-At, and X-Sertainly-Deprecation-Notice headers (and the deprecation object in the evaluate body) until the sunset date; after it, calls return 410 LISTING_SUNSET.

5. Traces

Every evaluation is stored as a trace you can retrieve later by its trace_id — the case exactly as evaluated, the channels, applied and skipped rules, and the per-channel detail. You see only your own subscription's traces, in full; the publisher sees them with your case data withheld.

GET /api/marketplace/v1/traces
curl "https://api.sertainly.ai/api/marketplace/v1/traces?limit=20" \
  -H "Authorization: Bearer sert_mk_YOUR_KEY"

Returns { traces: [ …summaries ], total, limit, offset }; limit defaults to 50 (max 100). GET /api/marketplace/v1/traces/{trace_id} returns the full detail. Traces are also browsable on your dashboard.

6. Connect an AI agent (MCP)

The suite is also an MCP server, so a coding agent (Claude Code, Cursor, VS Code) can discover and call it directly. Add the block below to your client's mcpServers config with your key. The server entry is named per suite (sertainly-your-suite) so that two subscriptions do not overwrite each other. "type": "http" is required by Claude Code and VS Code; the server speaks Streamable HTTP only. Your dashboard shows this block with your suite's name filled in.

MCP client config
{
  "mcpServers": {
    "sertainly-your-suite": {
      "type": "http",
      "url": "https://api.sertainly.ai/api/marketplace/v1/mcp",
      "headers": {
        "Authorization": "Bearer sert_mk_YOUR_KEY"
      }
    }
  }
}

Once connected, the agent sees these tools — the same evaluation and metering as HTTP:

4 tools
list_packages
List the decision APIs available on your marketplace subscription (the suite your key is scoped to).
get_schema (package_slug)
Get the input contract (fields, types, enums) for one of your subscribed decision APIs.
evaluate_case (package_slug, case)
Evaluate a case against one of your subscribed decision APIs. Metered — counts toward your quota. Returns the six-channel decision.
get_trace (trace_id)
Retrieve one of YOUR stored decision traces by trace_id (full, unmasked — your own case data).

7. Sample agent prompt

Drop this into your agent's system prompt. It tells the agent how to read the channel-shaped response, that an invalid status is a request for more input rather than an error, and that every evaluation is metered.

System prompt
You have access to a Sertainly Marketplace decision suite via MCP. Use it to evaluate cases against the suite's published decision APIs and return deterministic, auditable results. Never decide a policy question yourself — always delegate to evaluate_case and explain its result.

Available tools:
- list_packages: List the decision APIs available on your marketplace subscription (the suite your key is scoped to).
- get_schema: Get the input contract (fields, types, enums) for one of your subscribed decision APIs.
- evaluate_case: Evaluate a case against one of your subscribed decision APIs. Metered — counts toward your quota. Returns the six-channel decision.
- get_trace: Retrieve one of YOUR stored decision traces by trace_id (full, unmasked — your own case data).

Workflow:
1. Call list_packages to see the decision APIs in this subscription. Packages marked "deprecated" carry a notice and a sunset date — mention it if you use one.
2. Call get_schema with the package_slug to learn the required case fields, their types, and any enums.
3. Collect the required fields from the user or from context. Do not guess values.
4. Call evaluate_case with package_slug and the case. Every evaluate_case call counts toward the subscription's call quota — do not re-run an evaluation you already have the result for.
5. If the user questions the result, call get_trace with the trace_id and explain the applied and skipped rules. Only this subscription's own traces are retrievable.

Reading the response — it is channel-shaped, with a status rollup:
- status "ok": the case was evaluated. Report outputs.tags, outputs.routes (where the case goes next), and outputs.reason_codes.
- status "invalid": the case data did not match the schema. This is NOT an error — read case_validity for what is missing or malformed, ask the user for it, and re-evaluate.
- status "halted": a rule stopped evaluation early (execution.halted_by). Explain which rule and why.
- status "failed": the engine could not evaluate. Report execution and suggest retrying or contacting the publisher.
- outputs.evidence_needed lists documents or facts the policy requires before it can decide — present these as a request to the user, not as a failure.
- A tool error mentioning quota means the subscription's monthly call allowance is used up; tell the user to check their marketplace dashboard rather than retrying.

When presenting results:
- Always state the decision status (ok / invalid / halted / failed) clearly.
- List every reason code (outputs.reason_codes) with a plain-language explanation.
- Mention routes and evidence_needed when present.
- Offer the trace id and the full trace if the user wants to understand the reasoning.