{"ok":true,"data":{"service":{"name":"flashcards-open-source-app","version":"v1","description":"Offline-first flashcards service with user-owned workspaces, a compact SQL agent surface, and direct media transfer URLs."},"authentication":{"type":"email_otp_then_api_key","sendCodeUrl":"https://auth.flashcards-open-source-app.com/api/agent/send-code","verifyCodeUrl":"https://auth.flashcards-open-source-app.com/api/agent/verify-code"},"capabilitiesBeforeLogin":["Read the complete public catalog snapshot, package detail, card previews, and package media download URLs"],"capabilitiesAfterLogin":["Load account context","Select a workspace","Inspect the published SQL surface through OpenAPI and SQL introspection","Read cards and decks through POST /agent/sql/query (read-only)","Write cards and decks through POST /agent/sql/execute (INSERT, UPDATE, DELETE)","Ingest JPEG, PNG, and WebP image bytes through the workspace-scoped image media endpoint","Upload and durably complete media assets through workspace-scoped direct transfer endpoints with retry-safe reconciliation","Read media asset metadata and create download URLs through workspace-scoped media endpoints","Preview and download portable workspace package ZIP exports, and preview or confirm ZIP imports","Preview and install published catalog package versions into a workspace"],"authBaseUrl":"https://auth.flashcards-open-source-app.com","apiBaseUrl":"https://api.flashcards-open-source-app.com/v1","surface":{"accountUrl":"https://api.flashcards-open-source-app.com/v1/agent/me","workspacesUrl":"https://api.flashcards-open-source-app.com/v1/agent/workspaces","sqlQueryUrl":"https://api.flashcards-open-source-app.com/v1/agent/sql/query","sqlExecuteUrl":"https://api.flashcards-open-source-app.com/v1/agent/sql/execute","mediaAssetImageIngestionUrlTemplate":"https://api.flashcards-open-source-app.com/v1/workspaces/{workspaceId}/media-assets/images","mediaAssetUploadSessionCreateUrlTemplate":"https://api.flashcards-open-source-app.com/v1/workspaces/{workspaceId}/media-assets/upload-sessions","mediaAssetUploadSessionPartsUrlTemplate":"https://api.flashcards-open-source-app.com/v1/workspaces/{workspaceId}/media-assets/upload-sessions/{sessionId}/parts","mediaAssetUploadSessionCompleteUrlTemplate":"https://api.flashcards-open-source-app.com/v1/workspaces/{workspaceId}/media-assets/upload-sessions/{sessionId}/complete","mediaAssetUploadSessionAbortUrlTemplate":"https://api.flashcards-open-source-app.com/v1/workspaces/{workspaceId}/media-assets/upload-sessions/{sessionId}/abort","mediaAssetMetadataUrlTemplate":"https://api.flashcards-open-source-app.com/v1/workspaces/{workspaceId}/media-assets/{mediaAssetId}","mediaAssetDownloadUrlTemplate":"https://api.flashcards-open-source-app.com/v1/workspaces/{workspaceId}/media-assets/{mediaAssetId}/download-url","workspacePackageExportPreviewUrlTemplate":"https://api.flashcards-open-source-app.com/v1/workspaces/{workspaceId}/packages/export/preview","workspacePackageExportUrlTemplate":"https://api.flashcards-open-source-app.com/v1/workspaces/{workspaceId}/packages/export","workspacePackageImportPreviewUrlTemplate":"https://api.flashcards-open-source-app.com/v1/workspaces/{workspaceId}/packages/import/preview","workspacePackageImportUrlTemplate":"https://api.flashcards-open-source-app.com/v1/workspaces/{workspaceId}/packages/import","catalogSnapshotUrl":"https://api.flashcards-open-source-app.com/v1/catalog","catalogPackagesUrl":"https://api.flashcards-open-source-app.com/v1/catalog/packages","catalogPackageDetailUrlTemplate":"https://api.flashcards-open-source-app.com/v1/catalog/packages/{packageSlug}","catalogPackageVersionCardsUrlTemplate":"https://api.flashcards-open-source-app.com/v1/catalog/package-versions/{packageVersionId}/cards","catalogPackageMediaDownloadUrlTemplate":"https://api.flashcards-open-source-app.com/v1/catalog/package-versions/{packageVersionId}/media-assets/{packageMediaKey}/download-url","catalogPackageMediaDownloadTemplate":"https://api.flashcards-open-source-app.com/v1/catalog/package-versions/{packageVersionId}/media-assets/{packageMediaKey}/download","catalogPackageInstallPreviewUrlTemplate":"https://api.flashcards-open-source-app.com/v1/workspaces/{workspaceId}/catalog/package-versions/{packageVersionId}/install/preview","catalogPackageInstallUrlTemplate":"https://api.flashcards-open-source-app.com/v1/workspaces/{workspaceId}/catalog/package-versions/{packageVersionId}/install"},"mcp":{"url":"https://mcp.flashcards-open-source-app.com/mcp","description":"Remote MCP server for AI clients that connect through custom connectors (for example Claude.ai or ChatGPT). Add the url as a custom connector and authorize through OAuth, then use the sql_query tool to read and the sql_execute tool to write cards and decks. Headless or CLI clients may instead send Authorization: Bearer fca_… using the agent API key from email_otp_then_api_key login (the same key as the REST agent surface), with no OAuth or browser needed.","authorization":{"type":"oauth2","authorizationServer":"https://auth.flashcards-open-source-app.com","authorizationServerMetadataUrl":"https://auth.flashcards-open-source-app.com/.well-known/oauth-authorization-server","protectedResourceMetadataUrl":"https://mcp.flashcards-open-source-app.com/.well-known/oauth-protected-resource"},"apiKeyBearer":{"type":"api_key","header":"Authorization","scheme":"Bearer","description":"Send the agent API key (fca_…) as a Bearer token for headless/CLI use; same key as the REST agent surface."}}},"links":{"websiteUrl":"https://flashcards-open-source-app.com/","privacyUrl":"https://flashcards-open-source-app.com/privacy/","termsUrl":"https://flashcards-open-source-app.com/terms/","supportUrl":"https://flashcards-open-source-app.com/support/","docsUrl":"https://flashcards-open-source-app.com/docs/"},"instructions":"Public catalog reads do not require authentication: use GET https://api.flashcards-open-source-app.com/v1/catalog for the complete normalized snapshot, GET https://api.flashcards-open-source-app.com/v1/catalog/packages to list or search published packages, GET https://api.flashcards-open-source-app.com/v1/catalog/packages/{packageSlug} for package detail, GET https://api.flashcards-open-source-app.com/v1/catalog/package-versions/{packageVersionId}/cards for card previews, GET https://api.flashcards-open-source-app.com/v1/catalog/package-versions/{packageVersionId}/media-assets/{packageMediaKey}/download-url for a backend-controlled media download URL, and GET https://api.flashcards-open-source-app.com/v1/catalog/package-versions/{packageVersionId}/media-assets/{packageMediaKey}/download for the media bytes. For authenticated workspace operations, start with POST https://auth.flashcards-open-source-app.com/api/agent/send-code using the user's email. After send-code, follow the returned instructions: normal accounts require the 8-digit email code, while configured review/demo accounts use a deterministic 8-digit placeholder and do not send email. Do not immediately replay send-code. Then POST https://auth.flashcards-open-source-app.com/api/agent/verify-code with the otpSessionToken, code, and label to obtain an API key. After login, call GET https://api.flashcards-open-source-app.com/v1/agent/me, then GET https://api.flashcards-open-source-app.com/v1/agent/workspaces?limit=100. If no workspace is selected for this API key, call POST https://api.flashcards-open-source-app.com/v1/agent/workspaces/{workspaceId}/select or create one with POST https://api.flashcards-open-source-app.com/v1/agent/workspaces using {\"name\":\"Personal\"}. After workspace bootstrap, call GET https://api.flashcards-open-source-app.com/v1/agent/me and use data.agentWorkspaceReplicaId as lastModifiedByReplicaId when creating media assets and catalog installs. Use POST https://api.flashcards-open-source-app.com/v1/agent/sql/query for all shared card and deck reads (SHOW TABLES, DESCRIBE, SHOW COLUMNS, SELECT) and POST https://api.flashcards-open-source-app.com/v1/agent/sql/execute for all writes (INSERT, UPDATE, DELETE). For JPEG, PNG, or WebP images up to 4000000 bytes, prefer POST https://api.flashcards-open-source-app.com/v1/workspaces/{workspaceId}/media-assets/images with the image bytes as the request body and x-media-asset-id, x-media-created-at, x-media-client-updated-at, x-media-last-modified-by-replica-id, and x-media-last-operation-id headers; the backend normalizes to canonical JPEG bytes and returns the mediaAsset. For other media assets, create a multipart upload session with POST https://api.flashcards-open-source-app.com/v1/workspaces/{workspaceId}/media-assets/upload-sessions; if status is already_available, use the returned mediaAsset and skip byte upload. If create returns 503 MEDIA_ASSET_UPLOAD_SESSION_COMPLETION_IN_PROGRESS or MEDIA_ASSET_UPLOAD_SESSION_CREATION_IN_PROGRESS, wait for Retry-After and retry the same create request unchanged; do not start a parallel byte upload. If status is upload_required, request signed part URLs with POST https://api.flashcards-open-source-app.com/v1/workspaces/{workspaceId}/media-assets/upload-sessions/{sessionId}/parts, upload each part with the returned signed URL, method, and headers, then complete the upload with POST https://api.flashcards-open-source-app.com/v1/workspaces/{workspaceId}/media-assets/upload-sessions/{sessionId}/complete. If completion returns 503 MEDIA_ASSET_UPLOAD_SESSION_COMPLETION_IN_PROGRESS or MEDIA_ASSET_UPLOAD_SESSION_COMPLETION_DEADLINE_EXCEEDED, wait for Retry-After and retry the same completion with the same session and parts; do not abort or replace the upload. While a foreground writer is live, abort returns 503; while durable completion is pending or leased, abort returns 409. Both abort responses leave upload state and S3 unchanged, and replacement session creation returns 503 before creating another multipart upload. After reconciliation applies, retrying session creation returns already_available. Abort unused sessions with POST https://api.flashcards-open-source-app.com/v1/workspaces/{workspaceId}/media-assets/upload-sessions/{sessionId}/abort only when completion is not active or pending. Use GET https://api.flashcards-open-source-app.com/v1/workspaces/{workspaceId}/media-assets/{mediaAssetId} for registry metadata and GET https://api.flashcards-open-source-app.com/v1/workspaces/{workspaceId}/media-assets/{mediaAssetId}/download-url for a range-capable direct download URL. Use POST https://api.flashcards-open-source-app.com/v1/workspaces/{workspaceId}/packages/export/preview to preview a portable workspace package export, then POST https://api.flashcards-open-source-app.com/v1/workspaces/{workspaceId}/packages/export to download the ZIP. Use POST https://api.flashcards-open-source-app.com/v1/workspaces/{workspaceId}/packages/import/preview with application/zip bytes up to 4000000 bytes to preview a portable workspace package import; confirm with POST https://api.flashcards-open-source-app.com/v1/workspaces/{workspaceId}/packages/import as multipart/form-data with file ZIP bytes up to 4000000 bytes and an options JSON string. Use POST https://api.flashcards-open-source-app.com/v1/workspaces/{workspaceId}/catalog/package-versions/{packageVersionId}/install/preview to preview installing a published catalog package version and read its source tagCounts and defaultOptions. Confirm with POST https://api.flashcards-open-source-app.com/v1/workspaces/{workspaceId}/catalog/package-versions/{packageVersionId}/install and pass addImportTag, importTag, and removeTags to apply those choices alongside installId and operationIdPrefix. Treat installId as a workspace-scoped idempotency key: retry an ambiguous response with the same normalized request to receive the exact original result, and use a new installId only for an explicit repeat import. Reusing installId with a different request or colliding with unrelated operation ids is a conflict, not a successful replay. Omitting all three tag options preserves source tags without adding an import tag. The install creates normal cards in catalog ordinal order and logical media assets without copying object-storage bytes. For routine low-risk writes, a clear user request already counts as permission. Ask again only for risky or unclear actions. SELECT returns at most 100 rows per statement, and INSERT, UPDATE, and DELETE may affect at most 100 rows per statement. If you need more than 100 writes, split the work into multiple batches of at most 100 records across separate SQL statements or separate tool calls. Use https://api.flashcards-open-source-app.com/v1/agent/openapi.json for the published external agent contract. The SQL surface is intentionally limited and is not full PostgreSQL. If session creation returns 503 MEDIA_ASSET_UPLOAD_SESSION_COMPLETION_IN_PROGRESS or MEDIA_ASSET_UPLOAD_SESSION_CREATION_IN_PROGRESS, wait for Retry-After and retry the same create request unchanged; do not start a parallel byte upload. If completion returns 409 MEDIA_ASSET_UPLOAD_SESSION_EXPIRED, the backend has closed the expired session; create a fresh upload session and upload the bytes again instead of retrying the same completion request. For other completion or abort conflicts, follow error.code: MEDIA_ASSET_UPLOAD_SESSION_RESTART_REQUIRED means abort the legacy session if it is still open and create a fresh upload; MEDIA_ASSET_UPLOAD_SESSION_COMPLETED means reload or replay the completed asset and do not retry abort; MEDIA_ASSET_UPLOAD_SESSION_STATE_CONFLICT, MEDIA_ASSET_UPLOAD_MISMATCH, MEDIA_ASSET_UPLOAD_PROOF_MISMATCH, or MEDIA_ASSET_UPLOAD_NOT_FOUND means reload canonical session and media-asset state before acting, without blindly replaying or assuming rollback. If completion or abort returns MEDIA_ASSET_UPLOAD_SESSION_ACCESS_DENIED, WORKSPACE_ACCESS_DENIED, or MEDIA_ASSET_REPLICA_INVALID, reload workspace access, replicas, session, and media-asset state before retrying with restored access and a currently accessible replica; do not assume rollback. If completion or abort returns 404 MEDIA_ASSET_UPLOAD_SESSION_NOT_FOUND, reload canonical upload-session and media-asset state and verify sessionId before retrying or creating replacement state; the identifier may be wrong or stale. If completion or abort returns DATABASE_COMMIT_OUTCOME_UNKNOWN, reload or replay the exact action first because rollback is not guaranteed. MEDIA_ASSET_STORAGE_UNAVAILABLE, MEDIA_BLOB_LIFECYCLE_BUSY, and SERVICE_UNAVAILABLE may also follow admitted work; follow the action-specific error instructions and do not assume rollback.","docs":{"openapiUrl":"https://api.flashcards-open-source-app.com/v1/agent/openapi.json","docsUrl":"https://flashcards-open-source-app.com/docs/"}}