# Save Discover Later — Full Agent Manual > Exhaustive technical reference for autonomous agents using the Save Discover Later API. > Companion to /llms.txt. Machine-readable schema at /openapi.json. Last updated: 2026-04-30 Base URL: https://jnexdctcorewxreylfbi.supabase.co/functions/v1 Canonical site: https://savediscoverlater.com --- ## 1. Overview Save Discover Later is a programmatic bookmarking service. Agents POST URLs on behalf of human users; the platform enriches each save with metadata, tags, and AI summaries, then exposes them in the user's Library and (optionally) a public Save Box. The API follows the **Machine Payments Protocol (MPP)** — unauthenticated or unfunded calls return `402 Payment Required` with a `WWW-Authenticate: Payment` header per the Stripe Agentic Commerce specification. Agents may pay either by: 1. **Subscription**: `Authorization: Bearer ` tied to a Basic or Pro plan. 2. **Per-call**: `Payment: ` + `X-On-Behalf-Of: `. --- ## 2. Authentication ### 2.1 API keys (subscription) Generate keys at `/dashboard/settings` → Agent tab. Keys are prefixed `sdl_live_` and shown **once** at creation. The platform stores only a SHA-256 hash. Keys can be revoked instantly; revoked keys 402. ``` Authorization: Bearer sdl_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx ``` ### 2.2 Stripe SPT (per-call) Pay-per-link agents include a Stripe Shared Payment Token in the `Payment` header and the target user's UUID in `X-On-Behalf-Of`. Each successful save is metered at $0.05 USD. ``` Payment: spt_1OabcDEFghijKLMnopQRStuv X-On-Behalf-Of: 4f1c9e4a-1b2d-4c6e-9a3b-8d7e6f5a4c3b ``` --- ## 3. Endpoint reference ### POST /api-save Create one saved item. #### Request body | Field | Type | Required | Notes | |-------------|-------------|----------|---------------------------------------------| | `url` | string | yes | RFC 3986, http(s), ≤2048 chars | | `title` | string | no | ≤300 chars; defaults to URL | | `notes` | string | no | ≤1500 chars (~250 words) | | `tags` | string[] | no | ≤10 items, each ≤32 chars | | `space_id` | uuid | no | Adds the item to a Save Box | Total request payload must be ≤ **2 KB**. #### Success — 200 OK ```json { "id": "uuid", "url": "string", "title": "string", "notes": "string | null", "tags": ["string"], "source": "agent", "created_at": "ISO-8601", "charged_cents": 0, "payment_method": "subscription | metered | spt" } ``` --- ## 4. Error handling All errors return a JSON body of shape `{ "error": "", "reason"?: "...", "detail"?: "..." }`. ### 4.1 `400 invalid_url` / `invalid_json` The body could not be parsed, or `url` is missing/too long/not http(s). Action: validate locally before retrying. Do not retry the same payload. ### 4.2 `402 payment_required` — **Machine Payments Protocol** Returned when no credentials are supplied, the API key is invalid/revoked, the SPT is malformed, or the subscription is exhausted. Headers: ``` WWW-Authenticate: Payment realm="savediscoverlater", scheme="stripe-spt", price="5", currency="USD" Content-Type: application/json ``` Body: ```json { "error": "payment_required", "reason": "no_credentials | invalid_or_revoked_key | invalid_spt | missing_x_on_behalf_of_header", "price_cents": 5, "currency": "USD", "payment_url": "https://savediscoverlater.com/pricing", "docs": "https://savediscoverlater.com/llms.txt" } ``` Agent action: 1. Parse `WWW-Authenticate`. If `scheme="stripe-spt"`, mint a Stripe Shared Payment Token for the advertised `price` and `currency`. 2. Retry the original request with `Payment: ` and `X-On-Behalf-Of: `. 3. If you have an active subscription key, send `Authorization: Bearer ...` instead. 4. If `reason="invalid_or_revoked_key"`, **do not** retry — surface the error to the operator. ### 4.3 `405 method_not_allowed` Only `POST` (and `OPTIONS` preflight) are accepted. Do not retry with another verb. ### 4.4 `429 rate_limited` Returned when the per-key/per-token rate limit is exceeded. Headers: ``` Retry-After: X-RateLimit-Limit: 20 X-RateLimit-Remaining: 0 X-RateLimit-Reset: ``` Body: ```json { "error": "rate_limited", "retry_after_seconds": 12, "tier": "agentic" } ``` Agent action: back off with exponential jitter, honour `Retry-After`. Tier limits: | Tier | Per minute | Per hour | Per day | |---------|------------|----------|----------------| | Basic | 60 | 600 | — | | Pro | 120 | 1200 | — | | Agentic | 20 | 600 | 200 / SPT | ### 4.5 `500 save_failed` Internal database failure. Retry after ≥1s with exponential backoff, max 3 attempts. --- ## 5. Idempotency Agents that may retry should send `Idempotency-Key: `. Within 24 hours the same key returns the original response (and does not double-charge SPT calls). Without the header, retries may create duplicate saves. --- ## 6. Webhooks (optional) Configure in `/dashboard/settings` → Agent → Webhooks. | Event | Payload | |--------------------|------------------------------------------------------| | `save.created` | `{ id, url, user_id, source, agent_key_id }` | | `save.failed` | `{ url, error, agent_key_id, attempted_at }` | | `key.revoked` | `{ agent_key_id, revoked_at }` | | `spend.threshold` | `{ agent_key_id, period, amount_cents, threshold }` | Signature header: `X-SDL-Signature: t=,v1=`. --- ## 7. Operational guidance - **Discovery**: link to `/llms.txt` from the user-agent's system prompt. - **Versioning**: the API is unversioned; breaking changes are announced 30 days ahead in the `/docs/changelog` route and via `X-API-Deprecation` headers. - **Status**: `GET https://savediscoverlater.com/api/status` returns `{ "status": "operational" | "degraded" | "down", "checked_at": "..." }`. - **CORS**: `Access-Control-Allow-Origin: *`. Browser agents can call the endpoint directly. - **Privacy**: agent-saved items default to `private`. Public exposure requires the user to attach the item to a public Save Box. --- ## 8. Schema reference See `/openapi.json` (OpenAPI 3.1) for the canonical machine-readable schema including request/response models, error envelopes, and example payloads. --- ## 9. Changelog - **2026-04-30**: Initial public release of MPP, agentic tier, and `/llms-full.txt`.