Skip to content

Latest commit

 

History

History
795 lines (626 loc) · 30.6 KB

File metadata and controls

795 lines (626 loc) · 30.6 KB

API Reference

This repo currently contains two score-schema lineages:

  • canonical public/product read surface: scores
  • legacy engine/SQLAlchemy lineage: an_scores + dimension_scores

Unless explicitly noted otherwise, public/product-facing read flows should be understood as reading from scores. See docs/CANONICAL-SCORE-CONTRACT.md and docs/SCORE-CONTRACT-CONSUMER-AUDIT.md.

Scoring Endpoint

POST /v1/score

Legacy/internal scoring-engine endpoint.

Calculates an AN Score from explicit dimension inputs. In the legacy engine lineage, this path persists score records via the SQLAlchemy-backed scoring layer rather than the canonical public scores read surface.

Request body

{
  "service_slug": "stripe",
  "dimensions": {
    "I1": 9.5,
    "I2": 9.0,
    "I3": 8.5,
    "I4": 9.5,
    "I5": 9.0,
    "I6": 8.0,
    "I7": 9.0,
    "F1": 9.0,
    "F2": 9.5,
    "F3": 9.5,
    "F4": 8.5,
    "F5": 10.0,
    "F6": 9.0,
    "F7": 9.0,
    "O1": 9.0,
    "O2": 9.0,
    "O3": 8.0
  },
  "access_dimensions": {
    "A1": 6.0,
    "A2": 5.5,
    "A3": 6.0,
    "A4": 7.5,
    "A5": 8.0,
    "A6": 8.0
  },
  "evidence_count": 72,
  "freshness": "12 minutes ago",
  "probe_types": ["health", "auth", "schema", "load", "idempotency"],
  "production_telemetry": true,
  "probe_freshness": "18 minutes ago",
  "probe_latency_distribution_ms": {"p50": 120, "p95": 340, "p99": 620, "samples": 9},
  "hydrate_probe_telemetry": true
}

Response body

{
  "service_slug": "stripe",
  "score": 8.9,
  "execution_score": 9.1,
  "access_readiness_score": 8.4,
  "aggregate_recommendation_score": 8.9,
  "an_score_version": "0.2",
  "confidence": 0.98,
  "tier": "L4",
  "tier_label": "Native",
  "explanation": "Stripe scores 8.9 because idempotency supports safe retries, but auth flow friction interrupts agent autonomy.",
  "dimension_snapshot": {
    "dimensions": { "I1": 9.5, "...": 9.0 },
    "raw_weights": { "I1": 0.1, "...": 0.03 },
    "normalized_weights": { "I1": 0.1, "...": 0.03 },
    "category_scores": {
      "infrastructure": 8.9,
      "interface": 9.1,
      "operational": 8.7
    }
  },
  "score_id": "uuid",
  "calculated_at": "2026-03-03T22:11:00+00:00"
}

hydrate_probe_telemetry is optional. When true, the API auto-hydrates probe_freshness and probe_latency_distribution_ms from the latest stored probe result when those fields are omitted.

In v0.2, score remains a backward-compatible alias of aggregate_recommendation_score.

GET /v1/services/{slug}/score

Fetch the latest persisted score for a service from the current product-facing score surface. For the initial calibration set (stripe, hubspot, sendgrid, resend, github), this route can bootstrap from hand-scored fixtures when no DB row exists yet.

Search Endpoint

GET /v1/search?q=<query>&limit=<n>

Search indexed services by free-text query. Used by rhumb find <query>.

Response body

{
  "data": {
    "query": "payment routing",
    "results": [
      {
        "service_slug": "stripe",
        "name": "Stripe",
        "aggregate_recommendation_score": 8.9,
        "tier": "L4",
        "confidence": 0.95,
        "why": "Best default for payment flows with strong reliability."
      }
    ]
  },
  "error": null
}

limit is optional and can be used by clients to cap result count.

Capability discovery and routing

Use different endpoints for the vendor question and the action question:

  • GET /v1/search?q={query} answers who should I use? It returns services.
  • GET /v1/capabilities?search={query} answers what exact action slug should I call? It returns capability IDs such as search.query and email.send.

Recommended cold-start flow:

  1. Search services when you need to compare vendors.
  2. Search capabilities when you know the job but not the slug.
  3. Call GET /v1/capabilities/{capability_id}/resolve to see the ranked recommendation plus the default next step.
  4. Call GET /v1/capabilities/{capability_id}/execute/estimate to see the active execution rail, cost, and health before execution. Anonymous direct system-of-record rails also preserve machine-readable execute_readiness handoffs.

resolve and estimate intentionally answer different questions:

  • resolve is the ranked recommendation surface. Use execute_hint.preferred_provider and execute_hint.selection_reason when you need to explain why Rhumb wants a given provider first.
  • execute/estimate is the active-rail execution surface. The returned provider can differ from the top-ranked provider when a higher-ranked path is filtered by credential_mode, is not execute-ready, or is currently unavailable.

That difference is not drift. It is the contract telling you both the preferred overall provider and the selected provider on the current execution rail.

Example capability discovery flow:

curl "https://api.rhumb.dev/v1/capabilities?search=web+research"
curl "https://api.rhumb.dev/v1/capabilities/search.query/resolve"
curl "https://api.rhumb.dev/v1/capabilities/search.query/execute/estimate?credential_mode=rhumb_managed"

Resolve hint contract

GET /v1/capabilities/{capability_id}/resolve now returns an execute_hint block that is meant to answer the first-success question directly:

  • preferred_provider: the provider Rhumb wants the operator to use first
  • selection_reason: machine-readable explanation for why Rhumb chose that provider (highest_ranked_provider, configured_provider_preferred, higher_ranked_provider_unavailable, higher_ranked_provider_not_execute_ready, higher_ranked_provider_mixed_execute_blockers, or higher_ranked_provider_filtered_by_credential_mode)
  • skipped_provider_slugs: optional higher-ranked providers Rhumb intentionally skipped before choosing the execute path, including providers excluded by a requested credential_mode
  • unavailable_provider_slugs: optional subset of skipped higher-ranked providers that are currently breaker-blocked or otherwise unavailable for execute
  • not_execute_ready_provider_slugs: optional subset of skipped higher-ranked providers that still rank but cannot back execute in the current context
  • auth_method: the request-side credential handle (api_key, connection_ref, crm_ref, etc.)
  • configured: whether that path is already ready on the current deployment in the current context; when credential_mode is supplied, this is evaluated against that requested mode rather than some other supported mode
  • credential_modes_url: machine-readable handoff to the full per-mode setup matrix for this capability
  • preferred_credential_mode: the lowest-heroics credential mode for that provider in the current context
  • fallback_providers: optional ordered alternates that can also back execute right now when the preferred path is not the only viable choice
  • setup_hint: present when configured=false, with the exact next setup action Rhumb expects before execute
  • setup_url: present when Rhumb has a first-class setup surface for that mode, for example a provider ceremony route

fallback_chain stays as the ordered ranked shortlist, but now only includes providers that can actually back execute right now in the current context. Use GET /v1/capabilities/{capability_id}/credential-modes when you need the full per-mode matrix. Use execute_hint when you want the default next step plus any machine-readable alternates. If a requested credential_mode filters the provider list down to zero, resolve now keeps the 200 envelope but adds recovery_hint.reason=no_providers_match_credential_mode, recovery_hint.resolve_url, recovery_hint.credential_modes_url, and the unfiltered recovery_hint.supported_provider_slugs / recovery_hint.supported_credential_modes so callers can pivot without guessing. If the capability exists but no providers are registered yet, resolve returns recovery_hint.reason=no_providers_registered plus recovery_hint.resolve_url and recovery_hint.credential_modes_url so callers can distinguish missing catalog coverage from the other dead-end reasons. If the capability ID itself is wrong, resolve returns 404 capability_not_found plus resolution, search_url, and suggested_capabilities when Rhumb can rank close matches, so callers can recover from typos without a blind catalog search. If a requested credential_mode still leaves at least one provider, execute_hint.selection_reason and skipped_provider_slugs now stay honest about any higher-ranked providers that were filtered out, and provider-level plus execute-hint configured truth stays scoped to that requested mode so mixed-mode providers do not look preconfigured through the wrong rail. If a lower-ranked provider is still execute-ready after higher-ranked paths degrade, execute_hint now keeps the degraded handoff machine-readable too via unavailable_provider_slugs, not_execute_ready_provider_slugs, and the mixed blocker selection reason when both conditions apply. When a requested credential_mode dead-ends, whether because zero providers match or because the filtered set collapses to zero execute-ready paths, resolve keeps the recovery handoff machine-readable. recovery_hint.resolve_url gives callers the canonical rerun target for the same capability, recovery_hint.supported_provider_slugs and recovery_hint.supported_credential_modes still reflect the broader unfiltered pivot, recovery_hint.alternate_execute_hint carries the exact broader-rail execute/setup handoff when Rhumb can already identify one, and recovery_hint.setup_handoff carries the next ranked setup action when no executable alternate rail exists yet, so callers can still move forward without another blind search. In the degraded-but-still-ranked case, resolve also keeps the ranked providers list while returning fallback_chain=[], execute_hint=null, and recovery_hint.reason=no_execute_ready_providers plus degraded-provider context like recovery_hint.unavailable_provider_slugs and recovery_hint.not_execute_ready_provider_slugs.

Example recovery payload when credential_mode=agent_vault dead-ends for email.send but Rhumb can already point you at the broader byok rail:

{
  "data": {
    "capability": "email.send",
    "providers": [],
    "fallback_chain": [],
    "related_bundles": [],
    "execute_hint": null,
    "recovery_hint": {
      "reason": "no_providers_match_credential_mode",
      "requested_credential_mode": "agent_vault",
      "resolve_url": "/v1/capabilities/email.send/resolve",
      "credential_modes_url": "/v1/capabilities/email.send/credential-modes",
      "supported_provider_slugs": ["resend", "sendgrid"],
      "supported_credential_modes": ["byok"],
      "alternate_execute_hint": {
        "preferred_provider": "resend",
        "endpoint_pattern": "POST /emails",
        "auth_method": "api_key",
        "configured": false,
        "credential_modes": ["byok"],
        "credential_modes_url": "/v1/capabilities/email.send/credential-modes",
        "preferred_credential_mode": "byok",
        "selection_reason": "highest_ranked_provider",
        "fallback_providers": ["sendgrid"],
        "setup_hint": "Set RHUMB_CREDENTIAL_RESEND_API_KEY environment variable or configure via proxy credentials"
      }
    }
  },
  "error": null
}

endpoint_pattern keeps the canonical method-plus-path shape (POST /emails, not just /emails), and /v2/capabilities/{capability_id}/resolve returns the same recovery structure with top-level resolve_url plus nested credential_modes_url values rewritten onto /v2.

Example typo-recovery payload when the capability ID is wrong:

{
  "error": "capability_not_found",
  "message": "No capability found with id 'email.sned'",
  "resolution": "Check available capabilities at GET /v1/capabilities or /v1/capabilities?search=...",
  "request_id": "req_example123",
  "search_url": "/v1/capabilities?search=email.sned",
  "suggested_capabilities": [
    {
      "id": "email.send",
      "description": "Send email through a provider-backed execution rail"
    },
    {
      "id": "email.validate",
      "description": "Validate an email address before send or enrichment"
    }
  ]
}

Example recovery payload when providers still rank but none can execute yet and Rhumb can only hand you a machine-readable setup handoff:

{
  "data": {
    "capability": "email.send",
    "providers": [
      {
        "service_slug": "resend",
        "recommendation": "preferred",
        "credential_modes": ["byok"]
      },
      {
        "service_slug": "sendgrid",
        "recommendation": "available",
        "credential_modes": ["byok"]
      }
    ],
    "fallback_chain": [],
    "related_bundles": [],
    "execute_hint": null,
    "recovery_hint": {
      "reason": "no_execute_ready_providers",
      "resolve_url": "/v1/capabilities/email.send/resolve",
      "credential_modes_url": "/v1/capabilities/email.send/credential-modes",
      "supported_provider_slugs": ["resend", "sendgrid"],
      "supported_credential_modes": ["byok"],
      "setup_handoff": {
        "preferred_provider": "resend",
        "auth_method": "api_key",
        "configured": false,
        "credential_modes": ["byok"],
        "credential_modes_url": "/v1/capabilities/email.send/credential-modes",
        "preferred_credential_mode": "byok",
        "selection_reason": "highest_ranked_provider",
        "setup_hint": "Set RHUMB_CREDENTIAL_RESEND_API_KEY environment variable or configure via proxy credentials"
      },
      "not_execute_ready_provider_slugs": ["resend", "sendgrid"]
    }
  },
  "error": null
}

Use recovery_hint.alternate_execute_hint when Rhumb already knows a live broader execute path. Use recovery_hint.setup_handoff when the best next move is still setup rather than execution.

Execute auth/payment handoff

When POST /v1/capabilities/{capability_id}/execute returns HTTP 402, Rhumb now includes the normal execute recovery fields plus a machine-readable auth_handoff block.

auth_handoff is the answer to: "What should the agent do next?"

  • recommended_path: default retry/setup path (governed_api_key today)
  • retry_url: canonical execute retry target
  • docs_url: canonical onboarding map
  • paths[]: structured alternate rails, each with:
    • kind
    • setup_url
    • retry_header
    • summary
    • requires_human_setup
    • automatic_after_setup
    • optional requires_wallet_support

Current product truth:

  • default path: governed API key via /auth/login, then retry with X-Rhumb-Key
  • wallet repeat traffic: wallet-prefund via /payments/agent, then retry with X-Rhumb-Key
  • zero-signup per-call: x402 via /payments/agent, then retry with X-Payment carrying tx_hash, network, and wallet_address; if your buyer only emits wrapped authorization payloads, use wallet-prefund instead of the direct per-call retry

That means agents should not treat a 402 as a generic dead end. They should either follow the structured next step automatically when their runtime supports the rail, or surface the exact human setup action when it does not.

Direct AUD-18 system-of-record execute rails that do not support x402 per-call payment yet, such as the direct CRM / support / DB / warehouse / deployment / storage / GitHub Actions paths, now return auth-only machine-readable onboarding on HTTP 401 instead of a bare header error or misleading x402 discovery. Those responses include auth_handoff, resolve_url, and credential_modes_url, but intentionally keep auth_handoff.reason=auth_required and auth_handoff.paths[]=[governed_api_key] so agents are not misled into thinking payment or anonymous x402 works on those direct rails today. GET /v1/capabilities/{capability_id}/execute for those direct rails now stays off the x402 preflight path too: without X-Rhumb-Key it returns the same auth-only 401 guidance, and with a valid key it returns POST-only guidance instead of x402 instructions. Anonymous GET /v1/capabilities/{capability_id}/execute/estimate stays available for provider/cost inspection, but direct rails now also include data.execute_readiness with the same API-key handoff so callers do not lose the next execute step after estimate.

Direct DB-Read Capabilities (AUD-18 Wave 1)

Rhumb now exposes three direct PostgreSQL read-first capabilities:

  • db.query.read
  • db.schema.describe
  • db.row.get

These run through the normal capability surface:

  • GET /v1/capabilities/{capability_id}
  • GET /v1/capabilities/{capability_id}/resolve
  • GET /v1/capabilities/{capability_id}/credential-modes
  • POST /v1/capabilities/{capability_id}/execute

Hosted credential posture

For hosted Rhumb, the only blessed DB credential path is credential_mode="agent_vault".

  • agent_vault = preferred: pass a short-lived signed rhdbv1. DB vault token in X-Agent-Token; compatibility fallback: pass a transient PostgreSQL DSN directly in X-Agent-Token, never stored by Rhumb
  • byok = env-backed connection_ref resolution via RHUMB_DB_<REF> on the server, intended for self-hosted/internal operator-controlled deployments only

Hosted env-backed connection_ref mode is intentionally disabled/hidden right now. If you are calling the hosted product, use agent_vault.

POST /v1/capabilities/db.query.read/execute

Execute a bounded, read-only SQL query against the caller's PostgreSQL database.

Signed token helper (trusted/operator flow)

Use scripts/build_db_agent_vault_token.py to mint a short-lived signed rhdbv1. token bound to a connection_ref and, optionally, an agent_id / org_id.

python3 scripts/build_db_agent_vault_token.py \
  --connection-ref conn_app_read \
  --dsn 'postgresql://user:pass@db.example.com:5432/app' \
  --agent-id agent_123 \
  --org-id org_456

The script reads the signing secret from RHUMB_DB_AGENT_VAULT_SECRET first, then AUTH_JWT_SECRET / RHUMB_ADMIN_SECRET as fallbacks, matching the DB execute runtime.

Example request (hosted / agent_vault)

curl -X POST http://localhost:8000/v1/capabilities/db.query.read/execute \
  -H "Content-Type: application/json" \
  -H "X-Agent-Token: rhdbv1.eyJ..." \
  -d '{
    "credential_mode": "agent_vault",
    "connection_ref": "conn_app_read",
    "query": "select id, email from users order by created_at desc limit 5"
  }'

Example response body

{
  "data": {
    "capability_id": "db.query.read",
    "credential_mode": "agent_vault",
    "provider_used": "postgresql",
    "row_count": 5,
    "rows": [
      {"id": "u_123", "email": "ada@example.com"}
    ]
  },
  "error": null
}

db.schema.describe and db.row.get share the same hosted credential posture and execution endpoint shape.

Direct AWS S3 Read-First Capabilities (AUD-18 Wave 1)

Rhumb now exposes three direct AWS S3 read-first capabilities:

  • object.list
  • object.head
  • object.get

These run through the normal capability surface:

  • GET /v1/capabilities/{capability_id}
  • GET /v1/capabilities/{capability_id}/resolve
  • GET /v1/capabilities/{capability_id}/credential-modes
  • POST /v1/capabilities/{capability_id}/execute

Credential posture

For the first S3 slice, Rhumb supports credential_mode="byok" only.

  • expected request handle: storage_ref
  • runtime resolution: env-backed RHUMB_STORAGE_<REF> bundle on the server
  • current posture: operator-controlled / self-hosted style proofing until a cleaner hosted vault shape exists
  • bounded public AWS buckets can also use auth_mode: "anonymous" for unsigned reads while still enforcing explicit bucket/prefix allowlists

Bundle shape:

{
  "provider": "aws-s3",
  "auth_mode": "access_key",
  "aws_access_key_id": "AKIA...",
  "aws_secret_access_key": "...",
  "region": "us-west-2",
  "endpoint_url": "https://<optional-s3-compatible-endpoint>",
  "allowed_buckets": ["docs-bucket"],
  "allowed_prefixes": {
    "docs-bucket": ["reports/"]
  }
}

endpoint_url is optional. Use it only when you need an explicit S3-compatible endpoint override in a bounded operator-proof environment. auth_mode is optional and defaults to "access_key". Set "anonymous" only for bounded public AWS proof targets where unsigned reads are intentional.

Use scripts/build_s3_storage_bundle.py to generate and validate that bundle against the product runtime parser before setting it on Railway:

AWS_ACCESS_KEY_ID=... \
AWS_SECRET_ACCESS_KEY=... \
AWS_REGION=us-west-2 \
python3 scripts/build_s3_storage_bundle.py \
  --storage-ref st_docs \
  --bucket docs-bucket \
  --prefix docs-bucket=reports/ \
  --railway

That prints the exact railway variables --set ... command for RHUMB_STORAGE_ST_DOCS.

For a bounded public AWS proof target, use --anonymous instead of access-key env vars:

python3 scripts/build_s3_storage_bundle.py \
  --storage-ref st_docs \
  --anonymous \
  --region us-east-1 \
  --bucket 1000genomes \
  --prefix 1000genomes=1000G_2504_high_coverage/additional_698_related/ \
  --railway

POST /v1/capabilities/object.list/execute

List objects within an allowlisted bucket/prefix.

Example request

curl -X POST http://localhost:8000/v1/capabilities/object.list/execute \
  -H "Content-Type: application/json" \
  -H "X-Rhumb-Key: $RHUMB_API_KEY" \
  -d '{
    "credential_mode": "byok",
    "storage_ref": "st_docs",
    "bucket": "docs-bucket",
    "prefix": "reports/",
    "max_keys": 10
  }'

POST /v1/capabilities/object.head/execute

Fetch metadata for one allowlisted object.

POST /v1/capabilities/object.get/execute

Fetch a bounded object body for one allowlisted object.

Use scripts/s3_read_dogfood.py for the full hosted proof bundle:

python3 scripts/s3_read_dogfood.py \
  --storage-ref st_docs \
  --bucket docs-bucket \
  --prefix reports/ \
  --key reports/daily.json \
  --summary-only \
  --json-out artifacts/aud18-s3-hosted-proof-<timestamp>.json

Direct Zendesk Ticket Read-First Capabilities (AUD-18 Wave 1)

Rhumb now exposes three direct Zendesk ticket read-first capabilities:

  • ticket.search
  • ticket.get
  • ticket.list_comments

These run through the normal capability surface:

  • POST /v1/capabilities/ticket.search/execute
  • POST /v1/capabilities/ticket.get/execute
  • POST /v1/capabilities/ticket.list_comments/execute

Credential posture

For the first Zendesk slice, only credential_mode="byok" is supported.

Requests must include a support_ref that resolves on the server to an env-backed bundle:

  • RHUMB_SUPPORT_<REF>

Bundle shape:

{
  "provider": "zendesk",
  "subdomain": "acme",
  "auth_mode": "api_token",
  "email": "operator@example.com",
  "api_token": "zd_api_token",
  "allowed_group_ids": [12345],
  "allowed_brand_ids": [67890],
  "allow_internal_comments": false
}

Bearer-token mode is also supported by the runtime parser:

{
  "provider": "zendesk",
  "subdomain": "acme",
  "auth_mode": "bearer_token",
  "bearer_token": "zd_bearer_token",
  "allowed_group_ids": [12345],
  "allowed_brand_ids": [67890],
  "allow_internal_comments": false
}

Helper + proof scripts

  • Audit local proof sources plus hosted support-surface state before claiming a hosted bundle exists:
    • python3 scripts/audit_support_proof_sources.py --provider zendesk --summary-only
  • Build and validate the bundle:
    • python3 scripts/build_zendesk_support_bundle.py --support-ref st_zd --subdomain acme --auth-mode api_token --email you@example.com --api-token "$ZD_API_TOKEN" --allowed-group-id 12345 --allowed-brand-id 67890 --railway
  • Run the hosted proof loop once the bundle is set:
    • python3 scripts/zendesk_read_dogfood.py --support-ref st_zd --ticket-id 123 --comments-ticket-id 123 --denied-ticket-id 456

The hosted proof artifact now preserves the same machine-readable preflight contract in both blocked/preflight-only and full-proof runs, so automation can keep the next setup context after success or denial checks too: configured, available_for_execute, resolve_handoff, resolve, and credential_modes.

Example request

curl -X POST http://localhost:8000/v1/capabilities/ticket.get/execute \
  -H "Content-Type: application/json" \
  -H "X-Rhumb-Key: $RHUMB_API_KEY" \
  -d '{
    "credential_mode": "byok",
    "support_ref": "st_zd",
    "ticket_id": 12345
  }'

The runtime enforces:

  • group/brand scope via the support_ref bundle
  • public-comments-only by default
  • bounded search and comment limits
  • honest provider attribution as zendesk

Direct Vercel Deployment Read-First Capabilities (AUD-18)

Rhumb now exposes two direct Vercel deployment read-first capabilities:

  • deployment.list
  • deployment.get

These run through the normal capability surface:

  • GET /v1/capabilities/{capability_id}
  • GET /v1/capabilities/{capability_id}/resolve
  • GET /v1/capabilities/{capability_id}/credential-modes
  • POST /v1/capabilities/{capability_id}/execute

Credential posture

For the first Vercel slice, only credential_mode="byok" is supported.

Requests must include a deployment_ref that resolves on the server to an env-backed bundle:

  • RHUMB_DEPLOYMENT_<REF>

Bundle shape:

{
  "provider": "vercel",
  "auth_mode": "bearer_token",
  "bearer_token": "vercel_pat",
  "allowed_project_ids": ["prj_123"],
  "allowed_targets": ["production"],
  "team_id": "team_abc"
}

allowed_targets and team_id are optional. Omitting allowed_targets means the bundle can access any target inside the allowlisted projects.

Helper + proof script

Use scripts/vercel_deployment_read_dogfood.py for the hosted proof bundle:

python3 scripts/vercel_deployment_read_dogfood.py \
  --deployment-ref dep_rhumb \
  --project-id prj_xkjVLZiODE5z9WVa9mNyMnNroJBf \
  --deployment-id dpl_XDnnZwuVtFCKtaqWxxRUNWLVBa63 \
  --allowed-target production

The hosted proof artifact now preserves the same machine-readable preflight contract in both blocked/preflight-only and full-proof runs, so automation can keep the next setup context after success or denial checks too: configured, available_for_execute, resolve_handoff, resolve, and credential_modes.

Example request

curl -X POST http://localhost:8000/v1/capabilities/deployment.get/execute \
  -H "Content-Type: application/json" \
  -H "X-Rhumb-Key: $RHUMB_API_KEY" \
  -d '{
    "credential_mode": "byok",
    "deployment_ref": "dep_rhumb",
    "deployment_id": "dpl_XDnnZwuVtFCKtaqWxxRUNWLVBa63"
  }'

The first slice stays deliberately bounded to read-only deployment metadata with explicit project scope and optional target bounds. It does not expose deploy, redeploy, rollback, or deployment-event log fetches.

Direct GitHub Actions Workflow-Run Read-First Capabilities (AUD-18)

Rhumb now exposes two direct GitHub Actions workflow-run read-first capabilities:

  • workflow_run.list
  • workflow_run.get

These run through the normal capability surface:

  • GET /v1/capabilities/{capability_id}
  • GET /v1/capabilities/{capability_id}/resolve
  • GET /v1/capabilities/{capability_id}/credential-modes
  • POST /v1/capabilities/{capability_id}/execute

Credential posture

For the first GitHub Actions slice, only credential_mode="byok" is supported.

Requests must include an actions_ref that resolves on the server to an env-backed bundle:

  • RHUMB_ACTIONS_<REF>

Bundle shape:

{
  "provider": "github",
  "auth_mode": "bearer_token",
  "bearer_token": "ghp_xxx",
  "allowed_repositories": ["cli/cli"]
}

Helper + proof script

Use scripts/github_actions_read_dogfood.py for the hosted proof bundle:

python3 scripts/github_actions_read_dogfood.py \
  --actions-ref gh_cli \
  --repository cli/cli \
  --run-id 24204222943

The hosted proof artifact now preserves the same machine-readable preflight contract in both blocked/preflight-only and full-proof runs, so automation can keep the next setup context after success or denial checks too: configured, available_for_execute, resolve_handoff, resolve, and credential_modes.

Example request

curl -X POST http://localhost:8000/v1/capabilities/workflow_run.get/execute \
  -H "Content-Type: application/json" \
  -H "X-Rhumb-Key: $RHUMB_API_KEY" \
  -d '{
    "credential_mode": "byok",
    "actions_ref": "gh_cli",
    "repository": "cli/cli",
    "run_id": 24204222943
  }'

The first slice stays deliberately bounded to read-only workflow-run metadata with explicit repository scope. It does not expose workflow logs, artifacts, dispatch, or mutation paths.

Pricing Endpoint

GET /v1/pricing

Returns Rhumb's current machine-readable public pricing contract.

Response body

{
  "data": {
    "pricing_version": "2026-03-18",
    "published_at": "2026-03-18",
    "public_pricing_url": "https://rhumb.dev/pricing",
    "canonical_api_base_url": "https://api.rhumb.dev/v1",
    "free_tier": null,
    "modes": {
      "rhumb_managed": {
        "margin_percent": 20
      },
      "x402": {
        "margin_percent": 15,
        "network": "Base",
        "token": "USDC"
      },
      "byok": {
        "label": "BYOK or Agent Vault",
        "upstream_passthrough": true,
        "margin_percent": 0,
        "passthrough_note": "Rhumb adds no markup in BYOK or Agent Vault mode. Provider charges bill directly to your provider account."
      }
    }
  },
  "error": null
}

The pricing contract intentionally omits unfinished volume-discount tiers. The provider-controlled pricing path covers both direct BYOK and Agent Vault when the capability supports that credential mode.

Probe Endpoints

POST /v1/probes/run

Run and persist one internal probe.

Example:

{
  "service_slug": "stripe",
  "probe_type": "schema",
  "target_url": "https://status.stripe.com/api/v2/status.json",
  "sample_count": 3,
  "trigger_source": "internal"
}

POST /v1/probes/schedule/run

Execute a batch run from seed specs (Stripe/OpenAI/HubSpot).

Example:

{
  "service_slugs": ["stripe", "openai"],
  "sample_count": 3,
  "base_interval_minutes": 30,
  "dry_run": false
}

Response includes cadence_by_service guardrails with:

  • base_interval_minutes (clamped to a minimum of 5 and maximum of 1440)
  • next_interval_minutes (failure-aware exponential backoff)
  • consecutive_failures
  • jitter_seconds (deterministic per service)

GET /v1/services/{slug}/probes/latest

Fetch the latest persisted probe result for a service (optional probe_type query param).

For probe_type=schema, metadata includes schema_signature_version=v2 and schema_fingerprint_v2, which are derived from nested response shape descriptors (semantic drift guardrail beyond top-level key lists).

GET /v1/alerts

Fetch probe-derived drift alerts.

Current primitive alert types:

  • schema_drift — latest schema fingerprint differs from previous schema probe
  • latency_regression — p95 health latency regressed beyond threshold versus previous probe

Optional query params:

  • limit (default 50, max 100)