From 85a97672e0076f6797071522996a0b6a2cbbbdc4 Mon Sep 17 00:00:00 2001 From: Cameron Hejazi Date: Thu, 19 Mar 2026 11:12:45 -0500 Subject: [PATCH] Add Quotient Skill --- quotient/references/api-reference.md | 95 ++++++++++++ quotient/references/bankr-preferred-flow.md | 45 ++++++ quotient/references/error-handling.md | 32 ++++ quotient/references/vanilla-x402-flow.md | 158 ++++++++++++++++++++ quotient/skill.md | 87 +++++++++++ 5 files changed, 417 insertions(+) create mode 100644 quotient/references/api-reference.md create mode 100644 quotient/references/bankr-preferred-flow.md create mode 100644 quotient/references/error-handling.md create mode 100644 quotient/references/vanilla-x402-flow.md create mode 100644 quotient/skill.md diff --git a/quotient/references/api-reference.md b/quotient/references/api-reference.md new file mode 100644 index 00000000..c501a0d2 --- /dev/null +++ b/quotient/references/api-reference.md @@ -0,0 +1,95 @@ +# Quotient API Reference (Skill-Focused) + +Runtime API base: `https://q-api.quotient.social/api/v1` + +Documentation/schema base: `https://dev.quotient.social` + +For canonical schema details, use `https://dev.quotient.social/api/v1/openapi.json`. + +## Access and Authorization + +- Monetized requests can be authorized with either: + - `x-quotient-api-key`, or + - x402 challenge/settle flow (`PAYMENT-REQUIRED` -> `PAYMENT-SIGNATURE`). +- API keys are created from the account/developer area after signup/login at `https://dev.quotient.social` (email or Google). +- New accounts include free credits for initial API usage. + +## Pagination Contract + +- `cursor` is opaque; pass it exactly as returned. +- Cursor values are bound to endpoint + sort + active filters. +- Reusing a cursor with different filters/sort returns `422 invalid_cursor`. +- Loop until `has_more` is false. + +## Endpoints + +### GET /api/v1/markets + +List covered markets with forecast status. + +Common params: +- `topic` +- `max_forecast_age` (hours, default `48`) +- `sort` (`updated_desc`, `volume_desc`, `signal_count_desc`) +- `cursor` +- `limit` (`1-50`, default `20`) + +### GET /api/v1/markets/mispriced + +Markets where Quotient odds diverge from market odds. + +Common params: +- `min_spread` (`0-1`, default `0.05`) +- `max_forecast_age` (hours, default `48`) +- `sort` (`spread_desc`, `spread_asc`, `volume_desc`, `updated_desc`) +- `cursor` +- `limit` (`1-50`, default `20`) + +### GET /api/v1/markets/{slug}/intelligence + +Full intelligence briefing for one market: +- `quotient_odds`, `market_odds` +- `bluf` +- `key_drivers` with citations +- `signals` +- `sentiment` +- `affected_equities` + +### GET /api/v1/markets/{slug}/signals + +Paginated signals for one market. + +Common params: +- `cursor` +- `limit` (`1-50`, default `10`) + +### GET /api/v1/markets/lookup + +Batch lookup by identifier. + +Use exactly one: +- `slugs` (comma-separated, max 10), or +- `condition_ids` (comma-separated, max 10) + +### GET /api/v1/equities + +Equities ranked by exposure to Quotient-forecasted markets. + +Common params: +- `cursor` +- `limit` (`1-50`, default `20`) + +### GET /api/v1/equities/{slug} + +Full equity detail with linked markets and impact data. + +## Common Error Codes + +- `401 invalid_api_key` +- `401 gateway_required` +- `402 payment_required` +- `403 insufficient_credits` +- `404 invalid_market` / `404 not_found` +- `422 invalid_request` +- `422 invalid_cursor` +- `429 rate_limited` diff --git a/quotient/references/bankr-preferred-flow.md b/quotient/references/bankr-preferred-flow.md new file mode 100644 index 00000000..39338b98 --- /dev/null +++ b/quotient/references/bankr-preferred-flow.md @@ -0,0 +1,45 @@ +# Bankr-Preferred x402 Flow + +Use this flow when the agent has Bankr wallet/signing capability available. + +This is an x402-specific path. If you are using `x-quotient-api-key` auth, you can call endpoints directly without the x402 challenge/settle loop. + +## Why Bankr First + +- Wallet provisioning is already handled in typical Bankr setups. +- Signing and submission tooling is streamlined for agents. +- Reduces integration friction for autonomous request loops. +- Bankr signing path requires `X-API-Key` credentials for Bankr Agent API calls (for example, `/agent/sign`). + +## Runtime Requirements + +- Runtime can call Bankr Agent API endpoints with `X-API-Key`. +- API key has Agent API access enabled and is not read-only, so typed-data signing is permitted. + +## Request Sequence + +1. Send request to a monetized Quotient endpoint with no payment header. +2. Receive `402 Payment Required` and parse `PAYMENT-REQUIRED`. +3. Produce a valid x402 payment signature using the Bankr-controlled wallet. +4. Retry the same request with `PAYMENT-SIGNATURE`. +5. Parse `PAYMENT-RESPONSE` from the successful response. + +## Required Headers + +- On paid retry: `PAYMENT-SIGNATURE` +- Optional idempotency extension if available from your client stack: `Payment-Identifier` + +## Practical Notes + +- Keep request method/path/query/body identical between initial and paid retry. +- Treat malformed challenge payloads as hard failures and do not guess values. +- If settlement succeeds, cache reusable session/payment state only if your client confirms it is valid. + +## Implementation Reference + +If your Bankr client does not provide native x402 request wrapping, use the shared implementation in: + +- `references/vanilla-x402-flow.md` -> "Concrete TypeScript Example (x402 Client Wrapper)" +- `references/vanilla-x402-flow.md` -> "Bankr-Compatible Signer Adapter (If Needed)" + +This gives Bankr and non-Bankr agents one common x402 execution path. diff --git a/quotient/references/error-handling.md b/quotient/references/error-handling.md new file mode 100644 index 00000000..bbf811d5 --- /dev/null +++ b/quotient/references/error-handling.md @@ -0,0 +1,32 @@ +# Error Handling (API Key and x402 Skill Paths) + +This file documents the error contract for both supported auth paths: API key and x402. + +## Status Codes + +- `200` - Success. +- `401 invalid_api_key` - API key missing, invalid, or revoked. +- `401 gateway_required` - Gateway auth requirement not satisfied. +- `402 payment_required` - Payment challenge issued; read `PAYMENT-REQUIRED`, sign, retry with `PAYMENT-SIGNATURE`. +- `403 insufficient_credits` - Account credits are exhausted. +- `404` - Resource slug not found. +- `422` - Invalid request parameters or cursor mismatch. +- `429` - Rate limited; back off and retry. +- `5xx` - Upstream or gateway transient failure; retry with bounded backoff. + +## API Key-Specific Failure Cases + +- `401 invalid_api_key`: rotate or replace key from the Quotient account/developer area. +- `403 insufficient_credits`: top up billing/credits or switch to x402 flow for paid requests. + +## x402-Specific Failure Cases + +- Missing `PAYMENT-REQUIRED` on `402`: treat as gateway/proxy error and stop. +- Missing `PAYMENT-RESPONSE` after paid success: treat response as incomplete; log request id. +- Payment signature rejected: request a new challenge and sign again. + +## Retry Guidance + +- Use exponential backoff with jitter for `429` and `5xx`. +- Do not retry `422` without correcting inputs. +- Keep retries idempotent and bounded. diff --git a/quotient/references/vanilla-x402-flow.md b/quotient/references/vanilla-x402-flow.md new file mode 100644 index 00000000..da42ac15 --- /dev/null +++ b/quotient/references/vanilla-x402-flow.md @@ -0,0 +1,158 @@ +# Vanilla x402 Flow (Non-Bankr Agents) + +Use this flow for agents that do not use Bankr wallet tooling. + +This is an x402-specific path. If you are using `x-quotient-api-key` auth, you can call endpoints directly without x402 payment headers. + +## Preconditions + +- Agent has access to a wallet capable of the required x402 signing scheme. +- Agent can parse response headers and retry requests with modified headers. +- Agent reads pricing metadata from `GET https://dev.quotient.social/api/public/pricing`. + +## Domain Rule + +- Execute all paid API requests against the gateway domain (`https://q-api.quotient.social`). +- Use `https://dev.quotient.social` for OpenAPI/schema discovery only. + +## Protocol Flow + +1. Send request without payment headers. +2. If status is `402`, read `PAYMENT-REQUIRED`. +3. Build payment payload from that challenge. +4. Sign it with your wallet. +5. Retry the same request with `PAYMENT-SIGNATURE`. +6. On success, inspect `PAYMENT-RESPONSE`. + +## Reliability Rules + +- Do not mutate path/query/body between challenge and paid retry. +- Use bounded retry with backoff on `429` and transient `5xx`. +- If signature is rejected, fetch a fresh challenge and rebuild payment payload. +- Treat signature creation as deterministic for a given challenge and signer. + +## Minimal Pseudocode + +```ts +const first = await fetch(url, init); +if (first.status !== 402) return first; + +const required = first.headers.get("PAYMENT-REQUIRED"); +if (!required) throw new Error("missing_payment_required_header"); + +const paymentSignature = await signX402Payment(required, wallet); + +const paid = await fetch(url, { + ...init, + headers: { + ...(init.headers || {}), + "PAYMENT-SIGNATURE": paymentSignature + } +}); + +const responseReceipt = paid.headers.get("PAYMENT-RESPONSE"); +return { paid, responseReceipt }; +``` + +## Concrete TypeScript Example (x402 Client Wrapper) + +This follows the x402 buyer quickstart pattern and automatically handles: +- `402` detection, +- `PAYMENT-SIGNATURE` construction, +- paid retry flow. + +```ts +import { x402Client, wrapFetchWithPayment } from "@x402/fetch"; +import { ExactEvmScheme } from "@x402/evm/exact/client"; +import { x402HTTPClient } from "@x402/core/client"; +import { privateKeyToAccount } from "viem/accounts"; + +const baseUrl = process.env.QUOTIENT_API_GATEWAY_BASE_URL!; +const evmPrivateKey = process.env.EVM_PRIVATE_KEY as `0x${string}`; + +const signer = privateKeyToAccount(evmPrivateKey); + +const client = new x402Client(); +client.register("eip155:*", new ExactEvmScheme(signer)); + +const fetchWithPayment = wrapFetchWithPayment(fetch, client); +const response = await fetchWithPayment(`${baseUrl}/api/v1/markets/mispriced`, { + method: "GET", + headers: { "Content-Type": "application/json" } +}); + +if (!response.ok) { + throw new Error(`request_failed:${response.status}:${await response.text()}`); +} + +const data = await response.json(); +const httpClient = new x402HTTPClient(client); +const settle = httpClient.getPaymentSettleResponse((name) => response.headers.get(name)); + +console.log("markets:", data.markets?.length ?? 0); +console.log("payment_settlement:", settle); +``` + +## Bankr-Compatible Signer Adapter (If Needed) + +If your Bankr client does not expose native x402 helpers, you can adapt Bankr's typed-data signing +to the same x402 wrapper flow. + +Requirements for this adapter path: +- Provide a Bankr API key through `X-API-Key`. +- Ensure that key has Agent API access enabled and is not read-only, so `/agent/sign` can execute `eth_signTypedData_v4`. + +```ts +import { x402Client, wrapFetchWithPayment } from "@x402/fetch"; +import { ExactEvmScheme } from "@x402/evm/exact/client"; + +type TypedDataRequest = { + domain: Record; + types: Record>; + primaryType: string; + message: Record; +}; + +async function createBankrSigner(apiKey: string) { + const meRes = await fetch("https://api.bankr.bot/agent/me", { + headers: { "X-API-Key": apiKey } + }); + if (!meRes.ok) throw new Error(`bankr_me_failed:${meRes.status}`); + const me = await meRes.json(); + const address = me.walletAddress as `0x${string}`; + + return { + address, + // x402 schemes need an EIP-712 typed-data signer. + async signTypedData(payload: TypedDataRequest): Promise<`0x${string}`> { + const signRes = await fetch("https://api.bankr.bot/agent/sign", { + method: "POST", + headers: { + "Content-Type": "application/json", + "X-API-Key": apiKey + }, + body: JSON.stringify({ + signatureType: "eth_signTypedData_v4", + typedData: payload + }) + }); + if (!signRes.ok) throw new Error(`bankr_sign_failed:${signRes.status}`); + const signed = await signRes.json(); + return signed.signature as `0x${string}`; + } + }; +} + +const bankrSigner = await createBankrSigner(process.env.BANKR_API_KEY!); +const client = new x402Client(); +client.register("eip155:*", new ExactEvmScheme(bankrSigner as never)); + +const fetchWithPayment = wrapFetchWithPayment(fetch, client); +const res = await fetchWithPayment( + `${process.env.QUOTIENT_API_GATEWAY_BASE_URL}/api/v1/markets`, + { method: "GET" } +); +``` + +Use the private-key signer path by default. Use the Bankr adapter path when your runtime only has +Bankr API signing access. diff --git a/quotient/skill.md b/quotient/skill.md new file mode 100644 index 00000000..5a83ed8a --- /dev/null +++ b/quotient/skill.md @@ -0,0 +1,87 @@ +--- +name: quotient-api +description: Accesses Quotient market intelligence through either x402 micropayments or API key auth, with explicit 402 challenge/settle handling when using x402. +--- + +# Quotient API Skill + +Use this skill when an agent needs Quotient market intelligence and must execute x402 payment flows correctly. + +## Base URLs and Responsibilities + +- `QUOTIENT_API_BASE_URL`: `https://dev.quotient.social` + - Use for docs/schema/pricing lookup only. +- `QUOTIENT_GATEWAY_BASE_URL`: `https://q-api.quotient.social` + - Execute runtime API requests here. + +## Access Model + +- Use x402 or API key for monetized requests. +- Prefer Bankr wallet tooling when available. +- Support vanilla SIWE/SIWX x402 clients as a first-class fallback. +- Include `x-quotient-api-key` when available; runtime requests can be authorized via either x402 payment handling or API key auth. +- If using Bankr signing (`/agent/sign`), provide a Bankr API key via `X-API-Key` with Agent API access enabled and signing permissions (not read-only). + +## Getting a Quotient API Key + +- Sign up (or log in) at `https://dev.quotient.social`. +- Account creation supports: + - email login + - Google login +- New accounts include free credits, so users can try the API before paying. +- After signup/login, create or copy a Quotient API key from the Quotient account settings/developer area. +- Pass the key to the agent via config/secrets as `x-quotient-api-key` (or equivalent env/secret wiring used by your client). + +## Operator vs Agent Signup Path + +- Preferred: human operator completes signup/login and key creation, then injects the API key into agent config. +- Optional self-serve agent path: if your runtime supports browser automation plus secure secret storage, the agent can perform signup/login at `https://dev.quotient.social`, generate a key, and store it for later requests. +- If interactive auth (email verification, Google OAuth, CAPTCHA, 2FA, policy prompts) cannot be completed programmatically, fall back to the human-operator path. + +## x402 API Call Checklist + +1. Send request to Quotient gateway without payment headers. +2. If response is `402`, parse `PAYMENT-REQUIRED`. +3. Sign payment and retry with `PAYMENT-SIGNATURE`. +4. On success, parse `PAYMENT-RESPONSE`. +5. Apply retry/backoff rules for `429` and transient `5xx`. + +## Required Preflight (Deterministic) + +Before the first API call in a session, fetch these discovery endpoints from `QUOTIENT_API_BASE_URL`: + +- `/api/v1/openapi.json` +- `/api/public/pricing` +- Treat OpenAPI as the schema/parameter source of truth. +- Treat pricing endpoint response as the gateway-backed runtime cost source of truth. +- Before each monetized request, refresh pricing if your cached pricing data is older than 5 minutes. + +## Canonical Endpoints and Discovery + +Discovery endpoints below are served from `QUOTIENT_API_BASE_URL`: + +- API docs UI: `/docs` +- OpenAPI: `/api/v1/openapi.json` +- Pricing discovery endpoint: `GET /api/public/pricing` +- AI index: `/llms.txt` + +## Core Endpoints + +All endpoint paths below are relative to `QUOTIENT_GATEWAY_BASE_URL`. + +- `GET /api/v1/markets` - covered markets with forecast status +- `GET /api/v1/markets/mispriced` - markets where Q diverges from market odds +- `GET /api/v1/markets/lookup` - batch lookup by slugs or condition IDs +- `GET /api/v1/markets/{slug}/intelligence` - full intelligence on a single market +- `GET /api/v1/markets/{slug}/signals` - paginated analyst signals for a market +- `GET /api/v1/equities` - equities affected by Q-forecasted markets +- `GET /api/v1/equities/{slug}` - full detail on an equity entity + +## References + +Reference docs below are served from `QUOTIENT_API_BASE_URL`: + +- API reference: `/skill/references/api-reference.md` +- Bankr-preferred x402 flow: `/skill/references/bankr-preferred-flow.md` +- Vanilla x402 flow: `/skill/references/vanilla-x402-flow.md` +- Error handling: `/skill/references/error-handling.md`