# EvidenceDeck — Agent API Reference # For AI agents acting on behalf of authenticated EvidenceDeck users. # All operations require the user's Supabase JWT Bearer token. # Row-Level Security enforces that users can only access their own data. ## Authentication EvidenceDeck uses Supabase Auth. To act on behalf of a user, you need their access_token. ### Sign In (obtain token) POST {SUPABASE_URL}/auth/v1/token?grant_type=password Headers: apikey: {SUPABASE_ANON_KEY} Content-Type: application/json Body: { "email": "", "password": "" } Response: { "access_token": "...", "refresh_token": "...", "user": { "id": "..." } } ### Using the token All subsequent requests require: Authorization: Bearer {access_token} apikey: {SUPABASE_ANON_KEY} Content-Type: application/json The SUPABASE_URL and SUPABASE_ANON_KEY are public values visible in the app's source. The user must provide their own email/password or access_token to authorize agent actions. --- ## Base URL All REST endpoints: {SUPABASE_URL}/rest/v1/{table_name} Supabase uses PostgREST. Filter syntax: ?column=eq.value, ?column=ilike.*search* --- ## Data Model ### debates A debate (or "deck") is the top-level container for all evidence cards. Columns: id (uuid), owner_id (uuid), title (text), slug (text), description (text|null), status (draft|ready|archived), default_aspect_ratio (16:9|1:1|9:16), theme (jsonb), created_at, updated_at ### sections Logical groupings within a debate (e.g., "Opening", "Rebuttal", "Cross-Exam"). Columns: id (uuid), debate_id (uuid), title (text), description (text|null), position (int), created_at, updated_at ### cards Individual evidence cards within a debate, optionally assigned to a section. Columns: id (uuid), debate_id (uuid), section_id (uuid|null), type (card_type), title (text), content (jsonb), speaker_notes (text|null), source_label (text|null), source_url (text|null), tags (text[]), is_favorite (bool), shortcut (text|null, "1"-"9"), position (int), created_at, updated_at ### Card Types and Content Schemas type: "title" content: { "heading": "string", "subheading": "string|null" } type: "image" content: { "image_url": "string", "alt_text": "string|null" } type: "image_caption" content: { "image_url": "string", "caption": "string", "alt_text": "string|null" } type: "quotation" content: { "quote": "string", "author": "string|null", "source": "string|null" } type: "scripture" content: { "text": "string", "reference": "string", "translation": "string|null" } type: "claim_response" content: { "claim": "string", "response": "string", "claim_label": "string|null", "response_label": "string|null" } type: "comparison" content: { "left_label": "string", "left_content": "string", "right_label": "string", "right_content": "string" } type: "bullet" content: { "items": ["string", "string", ...], "heading": "string|null" } type: "split_media_text" content: { "image_url": "string", "text": "string", "image_position": "left|right" } type: "holding" content: { "message": "string|null", "show_logo": true|false } --- ## Common Operations ### List all debates for the user GET /rest/v1/debates?select=*&order=updated_at.desc Returns: array of debate objects ### Create a new debate POST /rest/v1/debates Headers: Prefer: return=representation Body: { "title": "My Debate Topic", "slug": "my-debate-topic", "description": "Optional description", "status": "draft", "default_aspect_ratio": "16:9", "theme": { "backgroundColor": "#000000", "fontFamily": "Inter", "primaryColor": "#3b82f6" } } Note: owner_id is automatically set by RLS from the JWT. ### List sections in a debate GET /rest/v1/sections?debate_id=eq.{debate_id}&order=position.asc ### Create a section POST /rest/v1/sections Headers: Prefer: return=representation Body: { "debate_id": "{debate_id}", "title": "Opening Arguments", "position": 0 } ### List cards in a debate GET /rest/v1/cards?debate_id=eq.{debate_id}&order=position.asc ### List cards in a specific section GET /rest/v1/cards?debate_id=eq.{debate_id}§ion_id=eq.{section_id}&order=position.asc ### Create a card POST /rest/v1/cards Headers: Prefer: return=representation Body: { "debate_id": "{debate_id}", "section_id": "{section_id_or_null}", "type": "quotation", "title": "Key Expert Quote", "content": { "quote": "The evidence clearly shows...", "author": "Dr. Jane Smith", "source": "Journal of Evidence, 2025" }, "tags": ["expert", "statistics"], "source_label": "Journal of Evidence", "source_url": "https://example.com/paper", "position": 0 } ### Update a card PATCH /rest/v1/cards?id=eq.{card_id} Headers: Prefer: return=representation Body: (only fields to update) { "title": "Updated Title", "content": { "quote": "Updated quote text", "author": "Dr. Jane Smith", "source": "Updated source" }, "tags": ["expert", "updated"] } ### Delete a card DELETE /rest/v1/cards?id=eq.{card_id} ### Search cards by text (title or tags) GET /rest/v1/cards?debate_id=eq.{debate_id}&or=(title.ilike.*search_term*,tags.cs.{search_term}) ### Assign a keyboard shortcut to a card PATCH /rest/v1/cards?id=eq.{card_id} Body: { "shortcut": "1" } Valid shortcuts: "1" through "9", or null to unassign. ### Reorder cards (set position) PATCH /rest/v1/cards?id=eq.{card_id} Body: { "position": 3 } ### Mark card as favorite PATCH /rest/v1/cards?id=eq.{card_id} Body: { "is_favorite": true } --- ## Live Session Operations Live sessions power the output window. When a session is active, pushing a card_id to it updates the audience-facing display in real-time. ### Start a live session POST {SUPABASE_URL}/functions/v1/start-session Headers: Authorization: Bearer {access_token} Body: { "debate_id": "{debate_id}" } Response: { "session": { "id": "...", ... } } ### Push a card to the live output PATCH /rest/v1/live_sessions?id=eq.{session_id} Body: { "active_card_id": "{card_id}", "is_blank": false } ### Blank the output (hide current card) PATCH /rest/v1/live_sessions?id=eq.{session_id} Body: { "is_blank": true } ### End a session PATCH /rest/v1/live_sessions?id=eq.{session_id} Body: { "status": "ended" } --- ## Bulk Import Pattern To import a full debate with sections and cards in one operation: 1. Create the debate (POST /debates) -> get debate_id 2. Create sections in order (POST /sections for each) -> get section_ids 3. Create cards with correct debate_id, section_id, and position values Example: importing a prepared case with 3 sections and 20 cards: - POST debate -> debate_id - POST 3 sections with positions 0, 1, 2 -> section_ids - POST 20 cards referencing the correct section_ids, positions 0..N within each --- ## Common Agent Use Cases ### "Prepare my evidence deck from this document" 1. Parse the source document into logical sections and evidence items 2. Create a debate with a descriptive title 3. Create sections matching the document structure 4. For each evidence item, determine the best card type: - Direct quotes -> "quotation" - Scripture/verse -> "scripture" - Opponent claim + your rebuttal -> "claim_response" - Side-by-side data -> "comparison" - List of points -> "bullet" - Image evidence -> "image" or "image_caption" 5. Create cards with proper content schema, source attribution, and tags ### "Add a rebuttal card to my active debate" 1. GET /debates to find the active debate 2. POST a "claim_response" card with the opponent's claim and user's response 3. Optionally assign a keyboard shortcut for quick access ### "Organize my cards into sections" 1. GET /cards for the debate 2. Create appropriate sections 3. PATCH each card with the correct section_id ### "Find and update the card about [topic]" 1. GET /cards with ilike filter on title or tag matching 2. PATCH the matching card with updated content --- ## Security Notes - All requests are scoped to the authenticated user's data via Row-Level Security - An agent cannot access another user's debates, cards, or sessions - The anon key alone grants zero access to user data — a valid JWT is always required - Image uploads require using Supabase Storage API (separate from REST) - Never store or log the user's password — only use it once to obtain a token, then use refresh_token for subsequent sessions --- ## Rate Limits Supabase enforces standard rate limits. For bulk operations, batch creates in groups of 10-20 with brief pauses between batches. --- ## Discovery - AI Plugin manifest: https://evidencedeck.app/.well-known/ai-plugin.json - This file: https://evidencedeck.app/agents.txt - Human-readable overview: https://evidencedeck.app/llms.txt