Primate Intelligence API — Changelog
Sorted newest-first. Agent-readable summary of shipped changes, broken by date. Full field contracts: openapi.json · agents.md
2026-07-29 — docs-only — POST /v1/parse documented in openapi.json; GET /v1/analyses status-filter vocabulary documented
POST /v1/parseis now in the OpenAPI document (PRI-496 P0). The endpoint has been live for months (stateless prompt compiler:{prompt}→{compiled_query, parse_mode: "haiku"|"heuristic"}; auth optional; 30 req/min per IP; errorsempty_prompt400 /parse_failed422 /parse_unavailable503) but was undocumented — for agents, undocumented is nonexistent. No behavior change. Also added to the/llms.txtkey-endpoints list.GET /v1/analysesstatusfilter vocabulary documented:queued | preparing | analyzing | rendering | completed | failed | canceled(§13.2). The filter has always worked (unknown values → 400validation_failed); its accepted values were just not written down. Documented in the parameter description — the machine schema staystype: string, so nothing tightens contractually. Usestatus=analyzingfor currently-running analyses.- Zero contract changes — the public surface is frozen; this is documentation of already-shipped behavior.
2026-07-28 — additive — SDK 0.2.0 (TS + Python) and MCP 0.2.0: full parity with the July API rounds
- TypeScript SDK
@primate-intelligence/sdk@0.2.0(breaking — type corrections):AnalysisUsagefixed to the real contract{billed_seconds, credit_balance_after};StreamEndReasonfixed (addscanceled/ice_failed/media_timeout, drops never-returnedclient_disconnect);video_duration_snullable;Analysisgainslivemode+origin. New:client.credits.retrieve()(GET /v1/credits),client.creditPricing.retrieve(),analyses.validateOnly(),analyses.createBatch()/validateBatch(),detected_count+indeterminate_reasonon results, typed streaming contract in/browser(session_remaining_smetering,no_media_frameswarning union, typed detections,failure_diagnostic, typedresults_summary). Migrate off thebalance_s/results_summary.framesaliases before their ~2026-08-28 removal. - Python SDK
primate-intelligence==0.2.0(additive):client.credits+client.credit_pricingresources,analyses.validate_only(),analyses.create_batch()/validate_batch(), docstrings updated to the real result/streaming contracts. - MCP 0.2.0 (hosted
/mcp+ npm@primate-intelligence/mcp): three new tools —validate_analysis(free dry-run with cost estimate),create_analysis_batch(batch discount),get_credits(transaction ledger).get_analysis/wait_for_analysisdescriptions now documentdetected_count,indeterminate_reason, nullable duration, and the usage snapshot. The npm package catches up with the hosted server: every tool declaresoutputSchemaand returnsstructuredContent;wait_for_analysisreturns the{analysis, retry}envelope (the legacy_mcp_notefield merged into the analysis resource is gone from the npm package too).
2026-07-28 — failure_diagnostic hint wording: customer-appropriate, no internal references
failure_diagnostic.hint(onend_reason: "ice_failed") reworded. The hint previously referenced an internal server environment variable — meaningless (and confusing) to API consumers. It now states plainly which side the fault is likely on and what to do: when the server did not advertise a publicly reachable network candidate → “server-side configuration issue — nothing to change on your end; retry, and contact support if it persists”; when the server did advertise public candidates → “check your network allows UDP or TCP/443 outbound and that your client uses the TURN servers inice_servers”. Field shape unchanged ({local_candidates, hint}); only the hint text changed. Same sweep reworded the transient status messages sent on the signaling WS (inference_unavailable, capacity/restart rejections) to remove internal jargon.
2026-07-28 — Metering tick + results_summary field renames (deprecation aliases until ~2026-08-28)
- Metering tick:
balance_srenamed tosession_remaining_s(Streaming DX Finding 2). The value was always the seconds remaining in this session's credit reservation (the session cap) — the old name read like account credit balance, which lives atGET /v1/billing/credits(balance_seconds). Every tick now carries BOTHsession_remaining_s(canonical) andbalance_s(deprecated alias, identical value).balance_swill be removed ~2026-08-28 — migrate reads tosession_remaining_s. - Terminal
results_summary:framesrenamed toresult_frames(Streaming DX Finding 3). Results are sampled, not per-source-frame (adaptive inference cadence, roughly every 8th source frame under load), so the count is the number of result events emitted —result_framessays that;framesimplied a source-frame count. Both fields present (identical value);frameswill be removed ~2026-08-28 — migrate reads toresult_frames. - Docs: result-sampling semantics now documented in agents.md —
frame_numon live results is the source-frame index (gaps between consecutive values are normal);results_summary.result_framescounts result events; expect roughly 8–15 results/s depending on load. - No behavior changes — both renames are additive aliases for 30 days, then the deprecated names drop.
2026-07-28 — No-media warning: the server now tells you when your media is the problem
- New signaling-WS event:
warning {code: "no_media_frames"}— when the WebRTC transport connects but no decodable video frame arrives within 5 seconds, the server pushes{type:"warning", code:"no_media_frames", transport_connected:true, elapsed_s, packets_received, frames_decoded, hint}(re-warned once at 15s, then quiet; canceled by the first decoded frame). Previously the server stayed silent for the full media-timeout window (~50s) with zero client-visible signal.packets_receiveddistinguishes the two failure classes:0= your client is not sending media (track dead/muted);>0withframes_decoded: 0= media arrives but is not decodable (wrong codec, or a source producing no real frames — e.g. canvas capture without a draw loop). end_reason: "media_timeout"now carries the media counters infailure_diagnosticwhen a connected-but-no-media session ends without ever going live. Billed 0, as before.- Existing
warning {remaining_s}credit-exhaustion event is unchanged (distinguish by thecodefield — credit warnings have nocode).
2026-07-28 — TURN-over-TLS (turns:) relay advertised for TLS-only egress networks
- ICE server list now always includes a
turns:<host>:443?transport=tcprelay in session credentials. Twilio's NTS token response carriesturn:relays (UDP/TCP 3478, TCP 443) but noturns:URL, so clients behind TLS-only firewalls (datacenter/corporate egress allowing only TLS on 443) could never reach a relay and sessions failed withice_failed. The API now synthesizes theturns:entry from the existing relay host, reusing its ephemeral credentials. No-op when the provider already returns aturns:URL or in STUN-only fallback. No request/response field changes — theice_serversarray simply gains one entry.
2026-07-28 — MCP tools declare outputSchema + structuredContent; wait_for_analysis timeout shape cleanup
- All 7 hosted MCP tools now declare an
outputSchema(intools/listonPOST /mcpand on the static server card at/.well-known/mcp/server-card.json). Schemas are generated from the same Zod DTOs that validate the public /v1 responses (src/dto/schemas.ts), so they cannot drift from the real contract. Per the MCP spec, every tool result now also carriesstructuredContentconforming to its declared schema (the SDK validates each result at runtime); the human-readable JSON text content is unchanged. wait_for_analysisresponse shape changed (privacy-audit finding F-1): the tool now returns{ analysis, retry }— the analysis resource unmodified underanalysis, andretry: nullon terminal states orretry: { reason: "timeout", note }when the wait expired. The old behavior merged an undocumented_mcp_notefield into the analysis object on timeout, mutating a documented resource shape. Agents reading the analysis should useresult.analysis(orstructuredContent.analysis).queryfield description on analysis outputs now states explicitly that it is the compiled, deterministic interpretation of the caller's own prompt — a transparency feature (privacy-audit finding F-2, documentation only; no data change).- REST API surface unchanged — this release only affects the MCP endpoint metadata and the MCP-layer
wait_for_analysisenvelope.
2026-07-28 — OpenAI apps domain verification + explicit destructiveHint on read-only MCP tools
- New public endpoint:
GET /.well-known/openai-apps-challenge— returns the OpenAI apps domain-verification challenge token (OPENAI_APPS_CHALLENGE_TOKENenv var) as rawtext/plain; 404 when unset. Unauthenticated by design — OpenAI's plugin/apps portal fetches it to verify domain ownership (PRI-475). Not part of the product API surface. - MCP tool annotations: explicit
destructiveHint: falseadded to all five read-only tools (get_analysis,wait_for_analysis,list_models,get_usage,get_test_fixture) in both the livetools/listresponse and the static server card at/.well-known/mcp/server-card.json. Previously the hint was omitted (spec default istrue), which OpenAI's tool scanner flagged. No changes to tool names, descriptions, or input schemas.
2026-07-28 — Streaming DX: server ICE fix + trickle, honest end reasons, unified result contract, public examples repo
Transport (the critical fix)
- Server-side ICE candidates are now reachable from anywhere — the streaming server advertises its public address as a host candidate and trickles late-gathered srflx/relay candidates to the client as
icemessages after the answer SDP (previously candidates that outlasted the bounded gathering window were silently dropped, so relay-only clients — datacenters, CI, UDP-blocked networks, strict NATs — stalled inice: checkinguntil timeout while browser clients survived via peer-reflexive discovery). Keep consumingice {candidate}messages afteranswer. - Relay-only regression client in CI — every deploy is tested with an aiortc client that offers only TURN relay candidates (TURN-over-TCP:443), the datacenter/edge-fleet profile.
Honest end_reason (breaking-adjacent: two new enum values)
- New values:
ice_failed,media_timeoutonstream.end_reason(WSend {reason}and the terminal Stream resource). A session that never went live is nevercompleted— enforced server-side. - New nullable field:
failure_diagnosticon the Stream resource — onice_failed, carries{local_candidates, hint}(the server-side candidate summary) so transport failures are debuggable from the API alone. - Billing unchanged: never-live sessions bill exactly 0 (join/negotiation/queue free).
Streaming result contract unified with file analyses (breaking for undocumented internals)
result.detections[].answeris now lowercaseyes|no|indeterminate— the same enum as file-API results (was"Yes"/"No", an internal casing leak; enums never vary by transport).result.detections[].promptnow echoes exactly what you submitted — the internal|||COMPILED_QUERY:<base64>serialization no longer leaks into the public field.- Latency telemetry (
inference_ms,server_elapsed_ms, per-hop p50s, per-stagelatencybreakdown) is grouped under a documentedtimingobject. Internal scheduling fields (queue_fill,queue_length,is_warm,video_enabled) are no longer emitted. - Same normalization applies to
results_summary.last_detectionson the terminal resource.
Docs & examples
- Public examples repo: https://github.com/Primate-Intelligence/primate-examples — Python streaming client (aiortc, the "stream a file" recipe), browser webcam example, relay-only CI harness. The old private-repo examples link in the streaming guide is gone.
- agents.md gains a full Streaming section — lifecycle, signaling protocol incl. server trickle, the streaming result contract, the honest end_reason vocabulary, the datacenter client profile, and billing semantics.
2026-07-27 — PRI-482 round 8: one-clock docs — canonical docs served by the API, website proxies them, hardened doc-sync CI gate, count-confidence semantics clarified
Documentation architecture (the "separate clocks" fix)
- Single source of truth —
docs/agents.mdanddocs/changelog.mdship inside the API deploy artifact and are served athttps://api.primateintelligence.ai/docs/agents.md//docs/changelog.md. The website (https://primateintelligence.ai/docs/agents.mdand/docs/changelog.md) now proxies these API routes instead of serving a hand-copied duplicate. Spec, quickstart, and changelog update atomically with the code they describe — there is no copy step left to go stale. - RSS moves to the API —
GET /docs/changelog.xmlis generated fromdocs/changelog.mdat request time, so the feed can never drift from the changelog it is derived from. - Hardened doc-sync CI gate — a diff touching
src/routes/**,src/dto/paths.ts, or billing/pricing services (credit-ledger-service,billing-service,usage-meter-service,quota-service) now fails CI unless both a docs surface (docs/agents.mdordocs/openapi.json) anddocs/changelog.mdmove in the same diff. Previously route changes required only one doc surface and billing changes required none. - Nightly drift monitor — an external probe diffs the website-served docs against the API canonical and alerts on any byte mismatch (belt-and-suspenders against pipeline-level failures CI can't see).
Contract clarification
- Count-query confidence — agents.md (Result Contract + Count queries sections) and
llms.txtnow state explicitly: for count queries,confidenceapplies to the detected count itself — it is the model's confidence thatdetected_countis the correct number, not merely that something was detected.
2026-07-27 — PRI-482 round 7: batch validate_only, enriched batch pricing, llms.txt batch listing, docs-atomicity CI gate, result-contract confidence cross-reference
New capabilities
POST /v1/analyses/batchwithvalidate_only: true— dry-run for batch: parses all prompts, returnsanalysis_batch_preview(HTTP 200) with per-promptquery,parse_mode,assessable,estimated_seconds,estimated_cost_usd, anddiscount_pct; plus batch-level totalsestimated_total_secondsandestimated_total_cost_usd. No credits reserved, no jobs created. Estimates arenullwhen the video has no known duration.- Enriched batch pricing on real create —
POST /v1/analyses/batchresponsepricingblock now includesestimated_total_secondsandestimated_total_cost_usd(null when duration unknown), so callers can see the expected cost without a separatevalidate_onlyround-trip.
Docs & CI
llms.txtbatch endpoint —POST /v1/analyses/batchadded to the## Key endpointssection.- Docs-atomicity CI gate —
scripts/changelog-gate.tsnow enforces a second rule: any diff touchingsrc/routes/**orsrc/dto/paths.tsmust also updatedocs/agents.mdordocs/openapi.json. Bypass with[skip-docs-gate]in a commit message. - Result Contract confidence cross-reference —
result.confidencebullet in agents.md "Result Contract" section now notes that for count queries confidence reflects detection certainty (not count certainty), with a link to the Count queries section. - agents.md batch section — added
validate_onlydocumentation with worked JSON example. - OpenAPI —
validate_onlyadded toCreateAnalysisBatchRequest;AnalysisBatchPreviewresponse schema added;AnalysisBatch.pricingupdated with estimated fields.
2026-07-27 — PRI-482 round 6: batch discounts real, confidence semantics, key lifecycle in OpenAPI, 404 envelope, ledger source_type, historical balance freeze
New endpoints
POST /v1/analyses/batch— submit 2–10 prompts against the same video in one request. First prompt billed at full price; each additional prompt billed at 50%. Credits reserved per-prompt at the discounted rate (observable directly inGET /v1/creditsledger). Response includesanalyses[]array +pricing: {full_price_prompts, discounted_prompts, discount_pct: 50}. Registered in OpenAPI spec and documented in agents.md "Batch analyses & discounts" section.POST /v1/keys/upgradeandPOST /v1/keys/request/GET /v1/keys/request/{code}now registered indocs/openapi.json. These routes existed but were absent from the spec — they are the first two calls any agent makes.
Error handling
- 404 catch-all — unknown routes previously returned the framework-default
{"message":"Route ... not found"}. They now return the standard typed error envelope (code: "resource_not_found", same shape as all other errors). POST /v1/analyseswithpromptsarray now returns a helpful validation error pointing toPOST /v1/analyses/batchinstead of a generic "Unknown field".
Billing fixes
source_type: "analysis"for analysis debits — credit ledger rows for public-API analyses were labelledsource_type: "upload". They are now labelledsource_type: "analysis"(new DB constraint value). Migration 077 backfills existing reservation and transaction rows in dev. Agents readingGET /v1/creditswill seesource_type: "analysis"on analysis debits going forward.- Historical
credit_balance_afterfrozen — analysis rows settled before migration 075 had a nullbalance_after_secondssnapshot, causingcredit_balance_afterto drift as later analyses ran. Migration 078 stamps those rows with the current account balance, freezing the value.credit_balance_after: null(snapshot_unavailable) now only appears when the account balance itself is unavailable.
Docs & contract
- Confidence semantics for count queries — added a paragraph to agents.md "Count queries — the contract" section:
confidenceis the aggregate detection confidence (max of per-term values, [0,1]); forced to 0 whenanswer: "indeterminate";≥0.7reliable,0.4–0.69marginal,<0.4unreliable. - Changelog link in agents.md — added a dedicated "Changelog" section linking
/docs/changelog.md. snapshot_unavailablesemantics —usage.credit_balance_after: nulldocumented in agents.md Result Contract.- Walking fixture section added to agents.md "Action pipeline verification" (clip URL pending upload).
Migrations applied to DEV
077_analysis_source_type.sql— adds'analysis'tocredit_reservations.source_typeconstraint; backfills existing rows.078_freeze_historical_balance_after.sql— stamps nullbalance_after_secondson settled reservations with current account balance.
2026-07-27 — PRI-480 round 5: agent DX — versioned responses, immutable billing snapshot, truthful origin, pricing discovery
Breaking changes
analysis.originenum changed:user→api, new valueconsole. The round-2 entry below introducedorigin: "user" | "system"derived at read time fromsource_type— that heuristic misclassified console (dashboard) uploads assystemand recorded nothing.originis now stamped truthfully at job creation:api(public API),console(dashboard upload),system(internal — auto-probe, fixture seeding, run_prompt). Legacy rows without the column fall back to the heuristic (public_api*→api,upload→console, elsesystem). The field shipped yesterday with near-zero consumers.
New fields & headers
X-Api-Versionresponse header — every response (including errors) carries<package-version>+<git-sha>. If the value changes mid-session, a deploy happened — re-check this changelog before assuming a bug.usage.credit_balance_afteris now immutable. Previously it re-read the live account balance at GET time, so an old analysis's value mutated as later analyses ran. It is now a point-in-time snapshot stamped at settlement (credit_reservations.balance_after_seconds). Analyses settled before this deploy fall back to the live balance (previous behaviour).
Billing invariant
- System-initiated analyses are never billed.
origin: "system"jobs (e.g. internal run_prompt) never reserve or settle credits — enforced by a CI test auditing everyreserveCreditscall site.
Docs & discoverability
GET /v1/credit-pricing(public, no auth) is now in the OpenAPI spec, the agents.md Pricing section, and/llms.txt. Returnsprice_per_second_cents,signup_grant_seconds,card_grant_seconds— cost of an analysis =usage.billed_seconds × price_per_second_cents.- Count-query contract section added to agents.md:
detected_countis only meaningful whenansweris determinate; success/failure examples are schema-validated in CI. - Changelog is now CI-enforced — any push/PR diff touching
src/**must also update this file with a well-formed topmost## YYYY-MM-DD — <title>entry (scripts/changelog-gate.ts+changelog-gate.yml). - Late note (shipped earlier today in commit df7f0ce without an entry):
GET /docs/agents.mdandGET /docs/changelog.mdare now served publicly —/llms.txtreferenced both but they 404'd. - Changelog re-sorted newest-first (the 2026-07-24/22/18 entries were filed below 2026-07-09).
2026-07-27 — PRI-480 round 2: result invariants, synchronous metadata, billed_seconds, origin
New fields
analysis.origin—"user"(submitted via the public API) or"system"(generated internally, e.g. auto-probe or fixture seeding). Agents should normally only act onorigin: "user"analyses.
Behaviour fixes
- Result consistency invariants. A completed analysis returning
detected_count: 5+answer: "indeterminate"+indeterminate_reason: "nothing_detected"is now repaired at the API boundary: (a)nothing_detectedforcesdetected_countto 0, (b)indeterminateforcesconfidenceto 0, (c) determinateyes/noanswers never carry anindeterminate_reason. - Video metadata populated synchronously on
/complete.POST /v1/videos/:id/completenow awaits the ffprobe enrichment (15 s timeout) before building the response, soduration_s,width,height, andfpsare present in the response body and in immediateGET /v1/videos/:idreads. On timeout the endpoint returns the row as-is and enrichment continues in the background. billed_secondson list analyses now correct. TheGET /v1/analyseslist was queryingcredit_reservations.job_id(a column that does not exist). Fixed to join viajobs.reservation_id → credit_reservations.id, matching the single-GET path.usage.billed_secondsis now populated on all settled list items.
New endpoints
GET /llms.txt— plain-text LLM index following the llms.txt convention. No auth required. Lists the API base URL,/docs,/openapi.json, and changelog. Lets AI coding assistants discover the API surface without a browser.
Docs
- Query-type maturity note added to agents.md: presence and counting queries are mature/GA; action and temporal queries are beta (duration-sensitive); open-ended prompts are rejected with
unsupported_query_formrather than billed.
2026-07-27 — PRI-480: API hardening, credits, and fixture parity
Breaking changes
None.
New fields
result.video_duration_s— nownumber | null(was alwaysnumber). Returnsnullwhen the source video duration could not be determined (short uploads, metadata-less files). Agents must null-check this field.result.indeterminate_reason— why the answer isindeterminate. Enum:low_confidence— model saw something but confidence was below threshold; try a more specific prompt.nothing_detected— all confidence scores were 0; no subject detected in any frame.unsupported_query_form— open-ended or freeform query; rephrase as a closed yes/no or count question.duration_mismatch— inference result duration disagrees with source video duration (>20%); result is untrusted.
result.detected_count— integer count for count-intent queries ("how many X…").nullon yes/no queries.
New endpoints
GET /v1/credits— returns{ balance_seconds, grant_seconds, used_seconds, transactions: [...] }. Per-analysis transaction history. Lets agents audit every charge independently. No parameters needed; key-scoped.
New parameters
POST /v1/analyses→validate_only: boolean— parse + estimate cost, no GPU, no credits deducted. ReturnsAnalysisPreviewwithestimated_cost_seconds,query_type, andanswerabilitysignal. Use before committing to an analysis.
Behaviour fixes
- Action pipeline duration now deterministic. The action query path (
how many X…) was returning GPU wall-clock time asvideo_duration_sinstead of source video duration. For a 6s video this produced 40–75s billing. Root cause:duration_secondswas absent from the action result dict in the inference server; the API fell back to GPU wall-clock. Fixed in inference I-1 (inference commit4e0825a). detected_countnow populates for action queries. The count was computed by inference but not forwarded in the callback payload. Fixed in inference I-2.indeterminate_reasonforwarded from inference. When inference explicitly sets a reason, the API now respects it rather than re-classifying.- Duration invariant cross-check. If inference returns a duration >20% different from the admitted source duration, the API flags the result
indeterminatewithindeterminate_reason: duration_mismatchrather than silently returning wrong timing. - Open-ended query gate. Submitting a query the model cannot answer (open-ended form) now returns
answer: indeterminate,indeterminate_reason: unsupported_query_formimmediately, at zero cost, instead of burning GPU credits and returning an unreliable result. - Video metadata probed at upload-complete.
duration_seconds,width,height,fpsare populated on the video object immediately afterPOST /v1/videos/:id/complete. Fire-and-forget ffprobe enrichment runs after response is sent for non-faststart files.
Test fixture update
GET /v1/test-fixture now returns a fixtures[] array with two entries:
presence— forklift yes/no (existing fixture, unchanged)action— forklift count query; assertdetected_count >= 1
Legacy flat fields (test_video_url, test_prompt, expected_answer, expected_confidence_min) preserved for backwards compatibility.
2026-07-24 — PRI-477: billing cap, detected_count, error envelope
New fields
result.detected_count— integer count on count-intent queries (object path).analyses.listresponse — includesbilled_secondsper analysis.- Error envelope — all errors now return
{ error: { code, message, request_id } }(was bare{ error, message }).
Behaviour fixes
- Billing cap — per-analysis billing is capped at the source video duration. A slow GPU run (warmup, recompile) cannot overbill beyond the video length.
answer: indeterminate— when confidence is 0 and all term confidences are 0 the answer is nowindeterminateinstead ofno. Anothat can't be substantiated is indistinguishable from a model that saw nothing.usagedeprecated — theGET /v1/usageresponse is kept butusageinline on analysis objects has moved toGET /v1/credits.
2026-07-22 — PRI-476: inline usage, url-ingest probe, liveProgress
New fields
analysis.usage— inline{ billed_seconds, credit_balance_after }on completed analyses. Agents no longer need a second GET /v1/usage round-trip.- URL-ingest video probe —
POST /v1/videoswithsource_urlnow runs the same metadata probe that upload-complete uses.
2026-07-18 — Public API v1 GA
Initial public release of the REST API with:
POST /v1/sandbox— instantpv_test_key, no email, no cardPOST /v1/keys/upgrade— upgrade topv_live_+ 6,000s free grantPOST /v1/videos— presigned upload or URL ingestPOST /v1/analyses— submit prompt + video → analysisGET /v1/analyses/:id— poll or usePrefer: wait=60GET /v1/analyses— paginated listGET /v1/credits— balance + transactionsGET /v1/test-fixture— stable CI fixture
2026-07-12 — Credit notifications, ToS consent, CORS
New features
- Expiring-credits reminder email — sent once per credit lot, 7 days before expiry. Requires opt-in notification prefs.
- Low-balance email — fires once per episode (was: every 24h).
Behaviour fixes
- Admin API-key mint/rotate now stamps implied Terms of Service acceptance.
- Admins exempt from ToS gate on stream and upload endpoints.
- CORS preflight now allows
X-Primate-Clientheader (dashboard regression fix).
2026-07-09 — PRI-440/439: P11 hardening, pricing analytics, SDK 0.1.1
New features
- Brownout ladder + status page — spend anomaly auto-suspend with configurable ladder (
BROWNOUT_STATE). Public status reflected at/v1/status. - SSRF IP-pinning — all outbound URL fetches (e.g., source_url ingest) are blocked from internal RFC-1918 and link-local ranges.
- Key-hygiene redaction — raw key values are redacted from server logs after mint.
- C1 runtime measurement — per-analysis GPU wall-clock recorded for pricing analytics (Phase 5).
SDK
PrimateError.rawMessage— verbatim server message, for UI display. TS SDK + MCP bumped to 0.1.1 via npm OIDC Trusted Publishing.- Python SDK — PyPI Trusted Publishing workflow added (tag
sdk-py-v*).
2026-07-09 — additive — SDKs, MCP server, agent artifacts
- Official SDKs: TypeScript (
@primate-intelligence/sdk) and Python (primate-intelligence) — typed resources, registry-driven retries, auto idempotency keys,createAndWait(), webhook verification, pagination iterators, browserconnectStream(). - MCP server (
@primate-intelligence/mcp) with seven tools mirroring the spec. llms.txt/llms-full.txt+/.well-known/llms.txt; all docs pages served as raw markdown at/docs/*.md.- New documentation site: quickstart, agent quickstart, ten guides, Scalar API reference, error-registry anchors.
2026-07-09 — deprecation — Legacy pk_ API key prefix sunset prepared
- Existing
pk_keys remain valid through the launch migration. - New keys and rotations create
pv_live_/pv_test_keys only. - The 12-month
pk_sunset clock starts at production GA announcement after Matt approves the cutover; the exact date will be published in the versioning guide and dashboard before enforcement.
2026-07-08 — additive — Billing: credit purchases + auto-refill
GET /v1/credit-pricing(public): price per second, grant sizes, purchase options.- Dashboard credit purchases, saved payment methods, and threshold-triggered auto-refill.
GET /v1/billing/usage-summaryfor dashboard usage aggregation.
2026-07-07 — additive — Webhooks + usage meters
webhook_endpointsresource: CRUD, Standard-Webhooks signing, delivery worker with1m…24hretry schedule, deliveries API, redelivery, secret rotation with 24h dual-secret overlap, auto-disable after 72h of failures.- Event vocabulary:
video.ready,video.failed,analysis.completed,analysis.failed,analysis.canceled,stream.ended. - Per-request
webhookoverrides onPOST /v1/analyses/POST /v1/streams(unsigned). GET /v1/usagegeneric meters shape (credit_secondsbalance + period consumption).
2026-07-06 — additive — Streams public API
streamsresource (§4.8): create → signal (WS) → WebRTC → live per-frame results → end. Queueing with honestqueue_position/estimated_start_s, per-plan session caps, 5s metering ticks, credit reservation reconciled to the second.stream_already_active(409) error code registered.
2026-07-05 — additive — Client tokens
POST /v1/client_tokens: ephemeral browser-safepvct_tokens (60–900s TTL, scope subsets, optional video/stream binding). Signaling WS accepts client tokens only — never secret keys.token_expired(401) error code registered.
2026-07-04 — additive — Instant sandbox + auth cutoff
- Anonymous
POST /v1/sandboxissues instantpv_test_keys (IP-limited, 7-day expiry, fixture corpus, deterministic results) with a pre-seeded fixture video. - Metered credit enforcement on the live plane; open signup.
sandbox_limit_exceeded(429) error code registered.
2026-07-03 — additive — Public core: videos + analyses
videosresource: presigned upload (create → PUT → complete), SSRF-guarded URL ingest, cursor-paginated lists, delete with 409 protection.analysesresource: free-text prompts or structured queries,Prefer: waitsync sugar (cap 120s), live progress + queue position, cancel, deterministicresultcontract (answer/confidence/clips).GET /v1/models,GET /v1/errors(machine-readable registry), idempotency keys (JCS canonical body comparison, 24h replay window).
2026-07-02 — additive — OpenAPI spec
GET /v1/openapi.json— OpenAPI 3.1, generated from the DTO registry; CI drift +oasdiff breakinggates. The spec is the source of truth for docs, SDKs, and this changelog's compatibility promises.