Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
95 changes: 95 additions & 0 deletions quotient/references/api-reference.md
Original file line number Diff line number Diff line change
@@ -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`
45 changes: 45 additions & 0 deletions quotient/references/bankr-preferred-flow.md
Original file line number Diff line number Diff line change
@@ -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.
32 changes: 32 additions & 0 deletions quotient/references/error-handling.md
Original file line number Diff line number Diff line change
@@ -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.
158 changes: 158 additions & 0 deletions quotient/references/vanilla-x402-flow.md
Original file line number Diff line number Diff line change
@@ -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<string, unknown>;
types: Record<string, Array<{ name: string; type: string }>>;
primaryType: string;
message: Record<string, unknown>;
};

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.
Loading