> ## Documentation Index
> Fetch the complete documentation index at: https://docs.drdj.me/llms.txt
> Use this file to discover all available pages before exploring further.

# API Surface

> Route-family map for planning, testing, and safe AI-assisted changes.

This is a planning map, not a strict OpenAPI replacement.

## Primary Route Files

* `/server/routes/auth.ts`
* `/server/routes/deals.ts`
* `/server/routes/contacts.ts`
* `/server/routes/tasks.ts`
* `/server/routes/teams.ts`
* `/server/routes/public.ts`
* `/server/routes/screenshots.ts`
* `/server/routes/shortener.ts`
* `/server/routes/shortener-v1.ts`
* `/server/routes/agent.ts`
* `/server/routes/agent-oauth.ts`
* `/server/routes/agent-readiness.ts`
* `/server/routes/settings.ts`
* `/server/routes/ai-schema.ts`
* `/server/routes/copilot.ts`
* `/server/routes/adminCopilot.ts`
* `/server/routes/admin.ts`

## Mounted Families

* `GET/POST/... /api/auth/*`
* `GET/POST/... /api/deals/*`
* `GET/POST/... /api/contacts/*`
* `GET/POST/... /api/tasks/*`
* `GET/POST/... /api/teams/*`
* `GET /api/public/*` (public-facing)
* `GET/POST /api/screenshots/*` (auth protected)
* `GET/POST /api/shorten/*` and `GET /r/*`
* `GET/POST/... /api/shortener/v1/*` (standalone AI-agent shortener)
* `GET/POST/... /api/agent/*` (provider-neutral MCP bridge, including read-only templates under `/api/agent/templates/*`)
* `POST /api/agent/oauth/register|device_authorization|token`
* `GET /.well-known/*`, `GET /openapi.json`, and `GET /docs/api` (public agent discovery)
* `GET/PUT /api/settings` and `GET /api/settings/agent-connection`
* `GET /api/ai/schema|entities|actions`
* `GET /api/health` and `GET /api/readiness` (process liveness vs dependency readiness)
* `GET/POST/... /api/copilot/*` (per-user in-app copilot; acts as the signed-in user)
* `GET/PUT /api/admin/copilot/*` and `GET/PUT /api/admin/settings` (admin-only; mounted before the generic `/api/admin`)

## Team-Visible Contacts And Influencers

Default `/api/contacts` and `/api/influencers` list calls remain owner-only for
extension and agent compatibility. Dashboard pages opt into team visibility with
`scope=mine-and-shared` and `visibility=all|mine|shared|mutual`.

Team-shared contact and influencer rows include access metadata and mutual-owner
indicators. They are read-only unless owned by the viewer. Contact notes remain
private to the contact owner. Shared influencer revenue and shared deal amount
fields are redacted for non-owners unless an approved sensitive-access grant
opens the relevant scope.

## Team-Shared Templates

Message and deal templates remain owner-private until explicitly shared with a
team. `POST /api/teams/:id/share` accepts `entityType=message_template` and
`entityType=deal_template` for owned templates.

`GET /api/teams/:id/templates?kind=all|message|deal` returns templates shared
with that team. Shared message templates expose attachment metadata without raw
data URLs. Shared deal templates expose delivery planning fields while hiding
price from non-owners.

## Agent Discovery Surface

DealDash publishes machine-readable agent discovery before static SPA fallback
so unknown well-known URLs return real 404s instead of the app shell.

* `GET /.well-known/api-catalog`: `application/linkset+json`, points agents to OpenAPI docs, health, OAuth metadata, MCP server card, and Agent Skills.
* `GET /openapi.json`: generated OpenAPI 3.1 description for the public agent route family and OAuth bootstrap routes. The document also carries `x-oauth-*` discovery links on the root object and bearer auth scheme so agents can find protected-resource, authorization-server, registration, device authorization, and token endpoints before making an authenticated call.
* `GET /docs/api`: redirects to `https://docs.drdj.me/backend/api-surface`.
* `GET /.well-known/oauth-authorization-server`: OAuth Authorization Server Metadata for public agent clients.
* `GET /.well-known/openid-configuration`: compatibility alias that mirrors the OAuth metadata and notes that DealDash does not issue ID tokens.
* `GET /.well-known/oauth-protected-resource`: OAuth Protected Resource metadata for `/api/agent`.
* `GET /.well-known/jwks.json`: empty JWKS because agent access tokens are opaque and server-validated.
* `GET /.well-known/mcp/server-card.json`: MCP server card for the DealDash Agent Bridge.
* `GET /.well-known/mcp/drdj-shortener/server-card.json`: shortener-only MCP server card.
* `GET /.well-known/agent-card.json`: A2A Agent Card advertising the existing DealDash Agent Bridge as an authenticated HTTPS JSON agent interface.
* `GET /.well-known/agent-skills/index.json`: Agent Skills index with a SHA-256 digest.
* `GET /.well-known/agent-skills/dealdash-agent-bridge/SKILL.md`: public skill instructions for agents.
* `GET /.well-known/agent-skills/drdj-shortener/SKILL.md`: standalone shortener skill for one-call drdj.me link creation.
* `GET /.well-known/http-message-signatures-directory`: Web Bot Auth key directory. It returns an empty JWKS unless production Ed25519 key material is configured with `WEB_BOT_AUTH_PUBLIC_JWK_JSON` or `WEB_BOT_AUTH_JWKS_JSON`; when `WEB_BOT_AUTH_PRIVATE_KEY_PEM` or `WEB_BOT_AUTH_PRIVATE_KEY_BASE64` is present, the response is signed with HTTP Message Signatures.

Global responses include:

* `Link: </.well-known/api-catalog>; rel="api-catalog"; type="application/linkset+json"`
* `Link: </docs/api>; rel="service-doc"`
* `Link: </openapi.json>; rel="service-desc"; type="application/openapi+json"`
* `Content-Signal: ai-train=no, search=yes, ai-input=yes`

Public marketing pages support `Accept: text/markdown` content negotiation and
return `Content-Type: text/markdown; charset=utf-8` plus `Vary: Accept`.

DealDash does not expose agent-commerce protocols by default. `x402`, MPP, UCP,
ACP, and AP2 should be added only when DealDash has a real payable API or
checkout surface to advertise.

## Agent Bridge Contract

`/api/agent/*` is the canonical backend surface for MCP-capable agents. It is shared by Claude Code, OpenAI/Codex or ChatGPT MCP clients, OpenClaw adapters, and future agent hosts.

* `GET /api/agent/schema` returns the tool-family schema and approval tiers.
* Login-link auth starts with `POST /api/agent/auth/start`; the user opens `/agent/authorize/:requestId`; the agent polls `GET /api/agent/auth/status/:requestId?deviceCode=...`.
* Public OAuth clients register with `POST /api/agent/oauth/register`, start device authorization with `POST /api/agent/oauth/device_authorization`, and exchange approved device codes with `POST /api/agent/oauth/token`.
* Signed-in users can inspect persistent agent status through `GET /api/settings/agent-connection`; the `advancedAuth` and `persistentConnections` sections show token last-four/timestamps only, never raw tokens or hashes.
* Signed-in users can revoke a user-owned persistent agent token with `DELETE /api/settings/agent-connection/agents/:requestId`; revocation marks the auth request denied and clears stored token material.
* Login-link status may report `connectionState: "token_already_claimed"` in MCP output when a previous agent process already claimed the token. Agents should start a fresh approval link for the current session instead of claiming they are connected.
* Tool calls authenticate with `Authorization: Bearer <DEALDASH_AGENT_ACCESS_TOKEN>` for login-link tokens or `Authorization: Bearer <DEALDASH_AGENT_SERVICE_SECRET>` for operator-managed adapters.
* Unauthenticated `/api/agent/*` responses include `WWW-Authenticate: Bearer resource_metadata="https://dealdash.tech/.well-known/oauth-protected-resource"`.
* Login approval links prefer `APP_ORIGIN`; `RENDER_EXTERNAL_URL` and `SHORT_LINK_DOMAIN` are fallback origins only.
* Agents without MCP installed may start login-link auth directly by posting to `https://dealdash.tech/api/agent/auth/start`; normal setup does not require service secrets, default user IDs, or deployment provider keys.
* Login-link agent tokens are long-lived and renewable, defaulting to 30 days and capped at 365 days by `DEALDASH_AGENT_TOKEN_TTL_MS`; the token is returned to the approved agent once and should be stored only in the agent host.
* Validate MCP setup with `pnpm agent:mcp-install:check --json` before copying install instructions. The check also verifies any installed local Codex plugin cache mirrors the repo bundle so `linkshot_evidence` remains available to agents.
* Tool calls require `x-dd-request-id`. Operator-managed service-secret auth also requires `x-dd-acting-user-id`; `x-dd-channel` and `x-dd-actor-id` identify the adapter.
* Agent-routed deal create/update calls use the same persisted deal routes as the dashboard. Successful writes leave the normal domain activity row with payload-safe agent metadata (`agentSource`, request id, actor id, channel, operation, approval tier, and approval id) and a separate payload-safe `agent_tool_call` row with request metadata plus response-derived deal IDs, deal name, promo code, status, and LinkShot bulk-import summary IDs/counts when available. Agent-routed screenshot reserve/upload/link writes add payload-safe proof handles such as `screenshotId`, `shortId`, `dealId`, `videoId`, `sourceUrl`, `platform`, `detectedViews`, `pending`, and `uploaded`, without logging raw image data, storage paths, public image URLs, or rendered `shortUrl`/`directUrl` values.
* Repo-local MCP tool calls return successful object payloads both as `structuredContent` and as serialized JSON text for backward compatibility. MCP error results use `isError: true` with safe JSON text and must not echo authorization headers, service secrets, image data, or stored login tokens.
* Sensitive writes require `x-dd-approval-id` by default.
* `DEALDASH_AGENT_WRITE_CONFIRMATION_MODE=allow_non_delete_writes` allows non-delete Tier 2 writes to run without approval; delete operations always require approval.
* Approval confirmation is restricted to trusted adapters with `x-dd-approval-confirm-secret`.
* OpenClaw policy belongs in channel adapters; the backend remains provider-neutral.

Agent OAuth scopes are normalized and allowlisted before client registration or
device authorization is accepted. New full-bridge clients default to
`dealdash.read dealdash.write.low_risk dealdash.memory`; existing already-issued
tokens with no stored scope remain legacy-compatible. `dealdash.read` authorizes
read routes, `dealdash.write.low_risk` authorizes Tier 1 operational writes,
and `dealdash.write.sensitive` or `mcp` is required for sensitive routes before
the existing approval gate is considered. The approval page displays the stored
request snapshot: client name, client URL, requested scopes, expiration, and a
full-bridge warning when `mcp` is requested.
The public `GET /api/agent/schema` contract also advertises the default scope,
supported scopes, metadata endpoints, and route-scope policy so agent clients do
not need to infer authorization behavior from prose docs.

Allowlisted V1 families are short links, screenshots, deals, payments, tasks, contacts, influencers, activity, suggestions, templates, context, memory, and approvals. Admin/auth/team, runtime/filesystem, and bulk destructive tools are excluded.

## Standalone AI Shortener

`/api/shortener/v1/*` is the tiny drdj.me shortener surface for agents that only need URL or photo short links.

* `GET /api/shortener/v1/health`: public health + discovery hints for agents (no login).
* `POST /api/shortener/v1/links`: creates guest URL links without auth, or registered-agent links with customization and ownership.
* `POST /api/shortener/v1/photos`: registered-agent-only photo-to-short-link upload.
* `GET /api/shortener/v1/links/:shortId`: minimal public-safe metadata, with private fields returned only to the owner agent with `shortener.links:read`.
* `GET /api/shortener/v1/links/:shortId/stats/public`: public-safe stats for guest/public links only.
* `GET /api/shortener/v1/links/:shortId/stats`: private stats for the owner agent.
* `POST /api/shortener/v1/agent/register|device_authorization|token`: shortener-scoped agent registration and approval.
* `GET /api/shortener/v1/agents` and `GET /api/shortener/v1/settings`: authenticated DealDash dashboard control-plane data.

Use the `drdj-shortener` skill or `/plugins/drdj-shortener` MCP server when an agent only needs short links. Use the full DealDash Agent Bridge for CRM, deals, contacts, payments, tasks, broad screenshots, LinkShot view checks, context, memory, and approvals.
Registered shortener calls require exact `shortener.*` scopes. Broad bridge scopes such as `mcp` or `dealdash.read` do not authorize standalone shortener actions.

### Shortener Examples

Guest URL create:

```http theme={null}
POST /api/shortener/v1/links
Content-Type: application/json
```

```json theme={null}
{
  "url": "https://example.com/campaign",
  "title": "Campaign"
}
```

Successful guest response:

```json theme={null}
{
  "shortId": "abc123",
  "shortUrl": "https://drdj.me/r/abc123",
  "directUrl": "https://drdj.me/r/abc123",
  "mode": "guest"
}
```

Registered-agent create with customization:

```http theme={null}
POST /api/shortener/v1/links
Authorization: Bearer <shortener-agent-token>
Content-Type: application/json
```

```json theme={null}
{
  "url": "https://example.com/campaign",
  "slug": "spring-launch",
  "campaign": "spring",
  "tags": ["agent", "campaign"],
  "description": "Created by an approved agent"
}
```

Common error codes:

* `invalid_target_url`: target URL is malformed, private, local, or uses an unsafe scheme.
* `reserved_slug`: custom slug collides with app routes or reserved words.
* `auth_required_for_customization`: guest callers attempted custom slug, campaign, tags, private stats, or photo upload.
* `rate_limited`: guest create budget exceeded.
* `shortener_scope_not_authorized`: registered agent token is missing the required `shortener.*` scope.
* `not_found`: link does not exist or caller cannot access private metadata.

Production has the shortener database migration applied. For any new
environment, apply `/migrations/extend-shortener-for-ai-agents.sql` before
routing real traffic to `/api/shortener/v1/*`.

## Agent Context And Memory

These endpoints make existing DealDash data easier for MCP clients to find before they call narrower tools.

* `GET /api/agent/context/resources`: Tier 0 read that returns MCP resource hints such as `dealdash://context/search`, `dealdash://linkshot/view-logs`, and `dealdash://memory/active`.
* `GET /api/agent/context/search`: Tier 0 read across deals, screenshots, LinkShot view logs, short links, contacts, influencers, and active agent memory. Filters: `q`, `scope`, `types`, `limit`, and `offset`.
* `GET /api/agent/memory`: Tier 0 read for human-visible account-scoped memories.
* `POST /api/agent/memory`: Tier 1 write to create a durable memory after user approval.
* `PATCH /api/agent/memory/:id/archive`: Tier 1 reversible write. Memory is archived, not hard-deleted.

Memory records live in `agent_memories`, are scoped by `user_id`, and include source fields, tags, confidence, actor/channel provenance, and status. This is the controlled memory layer. pgvector semantic search is the next upgrade after embedding generation and cost/retention rules are configured.

## Agent Template Reads

These endpoints give agents first-class read access to reusable templates instead of forcing them to guess through context search.

* `GET /api/agent/templates/message`: Tier 0 read for message templates. Filters: `tag`, `q`, `limit`, and `offset`.
* `GET /api/agent/templates/message/latest`: Tier 0 latest message template, optionally filtered by `tag`.
* `GET /api/agent/templates/message/latest-story`: Tier 0 latest message template tagged `story`.
* `GET /api/agent/templates/message/:id`: Tier 0 read for one message template.
* `GET /api/agent/templates/deal`: Tier 0 read for reusable deal templates. Filters: `q`, `limit`, and `offset`.
* `GET /api/agent/templates/deal/latest`: Tier 0 latest deal template.
* `GET /api/agent/templates/deal/:id`: Tier 0 read for one deal template.

## LinkShot Data Visibility

The authenticated `/screenshots` page uses LinkShot data and adds review
surfaces for screenshots, due view checks, and view logs. LinkShot
bulk-import writes share the same `/api/deals/bulk-import/process` path across
the extension, website, and agent bridge so manual imports and agent imports
create view-check videos consistently.

### Screenshots

`GET /api/screenshots` defaults to owned-only records.

Optional query:

* `scope=mine-and-shared`: include screenshots owned by the signed-in user plus screenshots shared through active team membership.

Shared screenshot rows include ownership context such as owner, sharer, team, and shared date when available. Existing screenshot mutation rules still apply:
owned screenshots can use the current link/share/delete flows, while team-shared rows are read-only unless the current user owns the screenshot.

### Due Views

`GET /api/deals/due-views` keeps its default extension-compatible response.

Optional query:

* `scope=mine-and-shared`: include owned deal videos plus authorized team-shared deal records.
* `includeLogs=true`: attach recent parsed LinkShot view-log context.
* `includeScreenshots=true`: attach authorized screenshot references and counts.

Team-shared deal logs only appear when the deal is shared through team entity sharing with deal sharing enabled.

### View Logs

`GET /api/deals/view-logs` lists LinkShot view checks. The authenticated
website can correct owned rows by using the existing
`PATCH /api/deals/:dealId/views/:viewId` route. Team-shared rows are editable
only when the current user is team owner/admin for the shared deal; ordinary
team-shared rows remain read-only.

`PATCH/PUT /api/deals/:dealId/views/:viewId` validates update payloads before
mutation. `value` must be a finite non-negative integer, `checkedAt` must be a
valid date when supplied, `sourceUrl` must be a bounded valid URL or null,
`note` is bounded, and unknown fields are rejected with `400 VALIDATION_ERROR`.

Supported filters:

* `scope`
* `dealId`
* `videoId`
* `logId`
* `from`
* `to`
* `q`
* `limit`
* `offset`

Rows include checked date, total views, parsed per-platform values, missing platform labels, source URL, deal/promo context, owner/sharer context, screenshot evidence, and per-row `copyText` in the LinkShot extension completed-copy format. The response also includes `copyText`, `copyTextScope: "returned_logs"`, and `copyTextFormat: "linkshot-extension-completed-copy-v1"` for the returned page/filter. Agents can use `logId` to fetch and copy one exact view-log row; website deep links with `tab=logs&logId=...` open that row and select it for copying. If the operator clears the focused selection, refreshes do not reselect it unless the URL focuses a different `logId`. Date-only `from` and `to` filters cover full checked days, so `to=2026-04-10` includes logs checked later on April 10. Screenshot previews are returned only when the current user owns the screenshot or has an explicit screenshot share; otherwise the row reports a redacted attachment reference.

### Bulk View-Check Imports

`POST /api/deals/bulk-import/process` adds LinkShot view-check videos from pasted URL groups.

Request body:

* `postDate`: required date for the video post.
* `collections`: optional array of URL groups. Each group has `urls`, optional `promoCode`, optional `name`, and optional deal details (`price`, `viewsTarget`, `promoLink`, `contact`, and `notes`, either at the group level or inside `dealDetails`).
* `linksText`: optional pasted text. Blank lines separate people/groups.

Behavior:

* Matched owned active deals receive new `deal_videos` rows.
* Unmatched groups without promo codes are returned in `needsPromoCode` with `nextAction.retryParams`, required fields, optional fields, and an agent prompt for collecting the missing deal details.
* Groups with promo codes reuse an owned active deal by promo code when possible; otherwise an active owned deal is created with the supplied optional details and the video is added. The promo code identifies the target deal, so DealDash does not silently fall back to a different URL-matched deal.
* Team-shared deal matches remain read-only and are returned for promo-code review instead of being mutated.

`POST /api/agent/deals/bulk-import/process` exposes the same path as `deals.bulk_add_view_checks`. It is a Tier 1 logged operational write for adding LinkShot view-check videos and can run without approval. Agents should not use generic `deals.create` or `deals.update` for this workflow; when unmatched links return `needsPromoCode`, ask for the missing promo code plus any available deal details, then retry `deals.bulk_add_view_checks` with the supplied data.

Screenshot upload endpoints accept base64 raster images as PNG, JPEG/JPG, WebP, GIF, AVIF, or BMP. The default binary image limit is 40MB, with a 64MB JSON body limit for base64 request headroom. `SCREENSHOT_MAX_IMAGE_BYTES`, `DEALDASH_AGENT_MAX_IMAGE_BYTES`, and `API_JSON_BODY_LIMIT` can raise or lower limits per deployment. SVG, HEIC, and TIFF should be converted by clients or messaging adapters before upload.

## External Shares

`POST /api/shares/external` and `PUT /api/shares/external/:id` validate
payloads before ownership checks and database writes. `entityType` is limited to
`deal`, `contact`, or `influencer`; `entityId` must be a UUID; `expiresAt` must
be a valid date or null; `maxViews` must be a positive bounded integer or null;
`visibleFields` must be a bounded string array or boolean map; `isActive` must
be boolean; and password strings must be sane length when present. Invalid
payloads return `400 VALIDATION_ERROR`; ownership checks remain unchanged.

## Agent Bridge

`/api/agent/deals/view-logs` exposes the same read-only data and copy-text
contract as a Tier 0 `deals.view_logs` operation for MCP-capable agents. Agent
calls use either the login-link access token or an internal operator-managed
service secret, and the backend applies the same user/team visibility rules as
the website.

The MCP `linkshot_evidence` tool is a read-only proof-normalization layer for
searched or captured video evidence. Use `plan_video_search`, follow the
returned `requestReadiness` and `browserSearchTargets`, fill
`rankingInputTemplate` for each raw candidate, call
`audit_browser_candidate_intake` before ranking, call
`extract_transcript_cues` when transcript/caption/first-comment text is
available, then call `rank_search_results` or `rank_candidates`, then call
`plan_proof_capture` to produce explicit screenshot jobs, then pass current
captures as `proofScreenshots`, then call `plan_direct_proof_review` before
claiming confirmation, then call `proof_run_harness`, then call
`workflow_report`.
`requestReadiness` uses `linkshot-grillme-request-readiness-v1` to block
under-specified `/grillme` browser/search handoffs until the known date plus a
promo/query/account anchor are explicit, and it returns missing fields, the
operator question, ready command templates, and the no-write boundary.
Transcript/caption matches are ranking and navigation evidence only; persistent
view-log writes require direct sponsor-brand logo or spoken promo proof, source video
URL, screenshot id/share URL, view count, and `observedPromoAtSeconds`. The mock
validation commands are `pnpm agent:smoke:linkshot-workflow --mock --json` for
the MCP evidence chain and `pnpm agent:smoke:linkshot-browser --mock --json` for
browser-visible candidate extraction into that chain. Use
the final MCP report/readiness actions' top-level `verificationRows` and
`verificationLinks` for machine-readable operator handoff because they keep each
source video URL beside its screenshot id/link and write-gate state without
requiring agents to parse Markdown report text. Use
`proof_run_harness` before the final answer to classify already-existing rows,
duplicate candidates, date confidence, verifier status, and no-write gates from
candidate rows, current proof screenshots, and prior view-log/import rows.
Profile/grid screenshots are review evidence only unless their `sourceUrl`
matches the canonical source video URL. Use
`pnpm agent:preflight:linkshot-live --mock --json` to validate the no-write live
preflight shell. Add `--from-due-views` when the agent should seed candidate
URLs from read-only `deals.due_views` context instead of requiring a pasted
`--url`, or run it with explicit `--url`, `--requested-date`, and `--promo-code`
values to inspect real platform pages without screenshots, uploads, short
links, platform search, or persistent view-log writes. The
preflight returns `proofCapturePlan.jobs` for the next screenshot handoff while
keeping `liveWrites` and `proofCapture` false.

Separately, the default `pnpm agent:readiness:check --json` gate also runs
`pnpm agent:native-automation:check --json` and
`pnpm agent:smoke:primitives --mock --json`. The native automation catalog emits
`nativeAutomationFormat: dealdash-native-automation-catalog-v1`, verifies the
DJA-130 native/local replacement paths for MCP install, action coverage,
bounded collection, preflight, proof rehearsal, historical inventory, direct
HTTPS fallback, GBrain-safe context, setup-kit parity, and DB audit coverage,
and keeps `liveWrites=false` plus `persistentViewLogWrites=false`. It also
checks the Apify policy remains documented: native/local first, exact
Actor/resource verification before any Apify use, no invented actor names, and
escalation after one blocked retry. The primitive JSON contract must expose
each core capability separately: `contextSearch`, `dueViews`, `viewLogs`,
`urlShortLink`, `imageUploadShortLink`, `screenshotCapture`, and a three-row
`verificationLinks` set. This keeps the source URL, returned short link,
uploaded image short link, and specified screenshot capture proof visible before
a larger `/grillme` search can claim readiness.

Add `--from-plan-targets --discover-candidates` only when
`plan_video_search.browserSearchTargets` includes a URL-bearing `account_page`
target. The preflight reports `planTargetSeed`, opens only supplied URLs plus
planned account-page URLs, and does not run broad platform/web search.
Add `--discover-candidates` only when the supplied URLs are explicit
account/search pages to inspect for linked video cards; discovery rows are
deduped by video URL and must still pass the same no-write intake, ranking,
proof-capture, and direct-proof review gates.
Add `--enrich-discovered-candidates` only when sparse discovered cards need a
bounded detail-page read. It opens discovered candidate URLs up to
`--enrich-limit` (default 5) to fill caption, first-comment, transcript,
view-count, and direct-proof fields, while keeping screenshots, short links, and
persistent view-log writes disabled.
Use `pnpm agent:collect:linkshot-candidates --url <account-or-search-page> --requested-date YYYY-MM-DD --promo-code CODE --json`
when a bounded browser/search agent needs to collect compact candidate rows from
supplied pages before the preflight. The collector blocks local/private targets
unless explicitly allowed, performs no screenshots, uploads, short links, or
persistent writes, and emits `linkshot-candidate-json-v1` rows for
`--candidate-json`. Rows may include `screenshotSelector`, `waitForSelector`,
or `captureClip` hints so later proof capture can target the views card or a
known region. If a supplied account/search page has no linked video candidate,
the collector reports no candidate instead of treating the page as a source
video.
Detail-page extraction reads visible DOM first and then falls back to structured
video metadata from JSON-LD or known app-state/player JSON scripts for source
URL, account, title, caption/description, transcript, published date, video id,
and view count. When visible/structured transcript text is missing, it can read
standard caption/subtitle track files from DOM `<track>` tags or
app-state/player JSON such as YouTube timed-text `captionTracks`, and mark
candidates with `transcriptSource: caption_track`, track URL, kind, label,
language, and track source. Structured and caption-track transcript cues remain
navigation-only.
With explicit `--capture-proof`, the command consumes those jobs, captures proof
through `screenshots.capture_url`, and passes the returned screenshot metadata
into `workflow_report` as `proofScreenshots`. This mode may create screenshot
proof records and short screenshot links, but it keeps
`persistentViewLogWrites: false`. Per-candidate `screenshotSelector`,
`waitForSelector`, and `captureClip` hints from imported or extracted rows are
preserved on matching proof-capture jobs as `candidateCaptureHint`; an explicit
CLI `--selector` still overrides selector choice at capture time. If the
screenshot capture response omits `sourceUrl`, the preflight carries forward the
source video URL from the capture job/params before building verification rows.
The CI-stable proof-capture handoff rehearsal is
`pnpm agent:readiness:check --only proof_capture_rehearsal --json`. It imports
compact candidate rows through `--candidate-json`, captures one proof screenshot
in mock mode, matches `workflow_report` rows, reports
`directProofReviewPlan`, keeps source video links beside screenshot links, and
still leaves persistent view-log writes disabled. Use
`pnpm agent:preflight:linkshot-live --mock --discover-candidates --enrich-discovered-candidates --capture-proof --capture-limit 1 --json`
only when the environment has Playwright browsers installed and the operator
also needs to exercise supplied page discovery plus capped detail enrichment
before capture.
The preflight also exposes `verificationRowsFormat:
linkshot-live-preflight-verification-row-v1` and top-level `verificationRows`
so agents can present each source video URL beside its screenshot id/link, view
count/source, direct-proof status, direct-proof review status, suggested seek
seconds, review window seconds, no-write reason, missing write requirements, and
row-level verification links without parsing nested report text.
Use
`pnpm agent:preflight:linkshot-live --mock --from-plan-targets --discover-candidates --json`
to validate planned account-page discovery without broad search or writes.
Use
`pnpm agent:preflight:linkshot-live --mock --mock-structured-metadata --json`
to validate structured metadata fallback extraction without screenshots, short
links, or view-log writes.
Use
`pnpm agent:preflight:linkshot-live --mock --mock-caption-track --json`
to validate caption/subtitle track cue extraction without screenshots, short
links, or view-log writes.
Use
`pnpm agent:preflight:linkshot-live --mock --mock-app-state-caption-track --json`
to validate app-state/player JSON caption-track extraction without screenshots,
short links, or view-log writes.
The default `pnpm agent:readiness:check --json` gate runs both focused
browserless caption-track preflights; use
`pnpm agent:readiness:check --only preflight_caption_track,preflight_app_state_caption_track --json`
when validating transcript cue handling alone. These checks import
deterministic caption-track candidate fixtures instead of launching Playwright,
must keep `persistentViewLogWrites` and `proofCapture` false, and summarize
transcript cue counts without treating the cue as direct proof. The raw
`--mock-caption-track` and `--mock-app-state-caption-track` commands remain the
browser-backed extraction checks for runners with Playwright browsers installed.
The repo-local `pnpm agent:inventory:linkshot-screenshots` command is the
metadata-only inventory shell for historical screenshot examples. The default
readiness gate runs
`pnpm agent:inventory:linkshot-screenshots --mock --include-view-logs --date 2026-04-10 --json`
so dated history selection, metadata-only output, `verificationRows`, and
`linkshot-extension-completed-copy-v1` copy text stay covered by the main
`/grillme` health check.

See [LinkShot Extension](../extensions/linkshot-extension.mdx) for the website
quick view and [DealDash Agent Bridge Plugin](../internal/dealdash-agent-bridge-plugin.mdx)
for MCP usage.

## Agent Connection Settings

`GET /api/settings/agent-connection` is an authenticated, read-only Settings endpoint for Bring Your Own Agent setup. It returns safe status, the single DealDash Agent Bridge model, MCP setup instructions, direct HTTPS fallback endpoints, permission tiers, tool groups, future AI feature slots, and recent `agent_tool_call` logs scoped to the current user.

The safe setup payload includes a human guide URL, the private DealDash Agent Kit repo URL for authorized install/examples only, an agent-optimized docs URL, the MCP command/config path, and direct `/api/agent/*` fallback URLs. Public and browser-visible responses expose names and links only, never secret values.

The endpoint returns env names and boolean configuration flags only. It must not expose service secret values, approval confirmation secret values, raw request payloads, image data, payment payloads, or cross-account logs.

The payload includes `writePolicy.mode`, `nonDeleteWritesRequireApproval`, and `deleteWritesAlwaysRequireApproval` so agents and users can see whether write tools can run without repeated confirmation. Deletes must remain approval-gated in every mode.

## Copilot

The in-app copilot has two distinct surfaces. `/api/copilot/*` is the per-user
runtime that acts as the signed-in user. `/api/admin/copilot/*` is a separate
operator/admin surface that reads across users; it is gated by
`requireAuth + requireAdmin` and is mounted before the generic `/api/admin` in
`server/index.ts`. This admin surface is a documented exception to the per-user
RLS default: cross-user reads are allowed but every secret is redacted (BYOK key
ciphertext is never selected, only `hasByokKey:boolean`; learning `proposedArgs`
user data is only counted, never surfaced verbatim).

* `GET /api/admin/copilot/overview`: usage headline, learning aggregates, rotation snapshot, and free-cap.
* `GET /api/admin/copilot/usage`: paginated per-user free-model usage rollups. Filters: `from`, `to`, `search`, `page`, and `limit`.
* `GET /api/admin/copilot/provider-configs`: paginated per-user provider/model/BYOK-presence/consent, with all secrets redacted.
* `GET /api/admin/copilot/learning`: self-learning signal and graduated-memory aggregates across users.
* `GET /api/admin/copilot/rotation`: free-rotation failovers, per-model aggregates, and live cooldowns.
* `GET /api/admin/copilot/settings`: the copilot free-tier daily cap.
* `PUT /api/admin/copilot/settings`: update `copilotFreeDailyLimit` (`0` = unlimited; authoritative over the `COPILOT_FREE_DAILY_LIMIT` env).
* `GET /api/admin/copilot/mcp-servers`: list global admin-registered MCP servers, with auth headers redacted to `hasAuthHeader`.
* `POST /api/admin/copilot/mcp-servers`: register a global MCP server (URL SSRF-guarded; auth header encrypted).
* `PUT /api/admin/copilot/mcp-servers/:id`: update a registered MCP server.
* `PATCH /api/admin/copilot/mcp-servers/:id/enabled`: enable or disable a registered MCP server.
* `DELETE /api/admin/copilot/mcp-servers/:id`: remove a registered MCP server.
* `GET/PUT /api/admin/settings`: admin Settings system toggles (now persisted), including `maintenanceMode` and `allowPublicSignup`.
* `DELETE /api/auth/account`: self-only account deletion.

The in-app copilot does not inherit local Claude/Codex stdio MCP registrations.
For GBrain project memory, operators can either register a public HTTP/SSE
GBrain MCP endpoint in `/api/admin/copilot/mcp-servers` or set
`COPILOT_GBRAIN_MCP_URL` with optional transport/auth-header env vars. The
env-backed GBrain endpoint is URL-guarded, header-validated, and loaded before
DB-registered MCP rows so it remains available if the registry query fails.
Use HTTP when the endpoint needs a static auth header; unauthenticated/public
SSE is allowed, and duplicate DB rows for the env-backed URL are skipped.
When `DEALDASH_GBRAIN_REQUIRE_CLOUD=true` or `READINESS_CHECK_GBRAIN=true`,
`GET /api/readiness` also verifies the dedicated GBrain database by reading
`config.version`; Render uses this readiness route for deployment health.

The cross-user query layer is `server/lib/copilot/admin.ts`, kept deliberately
separate from the per-user `usage.ts`/`providerConfig.ts`/`learning.ts` functions
so the privilege boundary is explicit. Global admin config (the Settings toggles
and the copilot free-tier cap) lives in the admin-only `app_settings` table;
free-model failover events are persisted to the admin-only `copilot_rotation_events`
table. Both tables are deny-by-default RLS and are served only through these
admin-gated routes.

## Validation Rule

When changing any route family:

1. run targeted integration tests
2. run `pnpm check`
3. update docs for endpoint behavior changes
4. include risk note for auth/public exposure changes
