[
  {
    "path": "/addons/javascript-addons",
    "title": "Advanced Integration Help",
    "description": "Use built-in addons, API calls, and Spala support for advanced integration needs.",
    "section": "addons",
    "url": "https://docs.spala.ai/addons/javascript-addons/",
    "last_updated": "2026-07-08",
    "content": "Advanced Integration Help\n\nMost Spala projects do not need custom integration code. Start with built-in addons, External API Call, webhooks, and AI Copilot.\n\nUse The Dashboard First\n\n- Install a catalog addon when one exists.\n- Use External API Call for REST APIs.\n- Store credentials in project environment variables.\n- Ask AI Copilot to wire the integration into your backend flow.\n\nNeed something reusable?\n\n  If your team needs a private reusable integration or organization-specific catalog item, contact Spala support.\n\nAddons\n/addons\nBrowse ready-to-use integrations\nExternal API Steps\n/reference/steps/api\nCall external APIs from functions\nAI Copilot\n/features/ai-assistant\nDescribe the integration you need"
  },
  {
    "path": "/addons/catalog/anthropic",
    "title": "Anthropic",
    "description": "Integrate Anthropic's Claude AI models for message generation in your Spala flows.",
    "section": "addons",
    "url": "https://docs.spala.ai/addons/catalog/anthropic/",
    "last_updated": "2026-07-08",
    "content": "Anthropic\n\nThe Anthropic addon connects your Spala flows to Anthropic's Claude API. Use Claude models for conversational AI, content analysis, summarization, code generation, and other text-based tasks.\n\nInstallation\n\n1. Enable the Anthropic addon from the Addons panel\n2. Enter your Anthropic API key in the addon configuration\n\nConfiguration\n\nAvailable Steps\n\nCreate Message\n\nSends a message to a Claude model and receives a response.\n\nExample: Content Moderation\n\n// POST /api/moderate\n\n// Step 1: Create Message (Anthropic addon)\n// model: '<current-claude-model>'\n// system: 'You are a content moderator. Analyze the following text and respond with a JSON object: { \"safe\": boolean, \"reason\": string, \"category\": string }. Only respond with valid JSON.'\n// messages: [{ role: 'user', content: input.text }]\n// maxTokens: 200\n// Output → vars.moderation\n\n// Step 2: Set Variable - Parse the response\n// vars.result = JSON.parse(vars.moderation.content[0].text)\n\n// Step 3: Condition - Check if content is safe\n// If vars.result.safe === false\n//   Step 4: Database Query - Flag the content\n//   INSERT INTO flagged_content (text, reason, category) VALUES (...)\n\n// Step 5: Response\n// { safe: vars.result.safe, reason: vars.result.reason }\n\nExample: Document Summarization\n\n// POST /api/summarize\n\n// Step 1: Database Query - Get the document\n// SELECT * FROM documents WHERE id = input.documentId\n\n// Step 2: Create Message (Anthropic addon)\n// model: '<current-claude-model>'\n// system: 'Summarize the following document in 2-3 paragraphs. Focus on the key points and main conclusions.'\n// messages: [{ role: 'user', content: vars.document.content }]\n// maxTokens: 1000\n// Output → vars.summary\n\n// Step 3: Database Query - Save the summary\n// UPDATE documents SET summary = vars.summary.content[0].text WHERE id = input.documentId\n\n// Step 4: Response\n// { summary: vars.summary.content[0].text }\n\nResponse Format\n\nThe Create Message step returns Anthropic's standard response format:\n\n{\n  id: \"msg_...\",\n  type: \"message\",\n  role: \"assistant\",\n  content: [\n    {\n      type: \"text\",\n      text: \"The model's response text...\"\n    }\n  ],\n  model: \"<current-claude-model>\",\n  usage: {\n    input_tokens: 125,\n    output_tokens: 340\n  }\n}\n\nAccess the response text with: vars.result.content[0].text\n\n  Claude models excel at long-form content analysis, nuanced reasoning, and following complex instructions. They support context windows up to 200K tokens, making them suitable for processing large documents.\n\n  Anthropic API calls are billed per token. Set the maxTokens parameter appropriately for your use case to control costs.\n\nYour Anthropic API key from console.anthropic.com.\nThe Claude model to use. Choose a currently supported model from Anthropic.\nArray of message objects with 'role' and 'content' fields.\nSystem prompt that sets the behavior and context for the conversation.\nMaximum number of tokens in the response.\nSampling temperature between 0 and 1 (default: 1).\nAddons Overview\n/addons\nBrowse available addons\nInstalling Addons\n/addons/installing\nEnable and configure addons in your project"
  },
  {
    "path": "/addons/declarative-addons",
    "title": "API-Based Integrations",
    "description": "Use External API Call and project environment variables to connect REST APIs from Spala.",
    "section": "addons",
    "url": "https://docs.spala.ai/addons/declarative-addons/",
    "last_updated": "2026-07-08",
    "content": "API-Based Integrations\n\nFor services that expose an HTTP API, use the External API Call step from your function, endpoint, task, or trigger. This covers most custom integration needs without custom platform code.\n\nHow It Works\n\n1. Add the required API key in Settings > Environment.\n2. Add an External API Call step to your function.\n3. Configure the method, URL, headers, and request input.\n4. Store the response in a variable.\n5. Return the response, save data to a table, or continue the workflow.\n\nKeep secrets server-side\n\n  Reference secrets from project environment variables. Do not paste secret API keys into frontend code.\n\nGood Fits\n\n- CRM updates\n- Notification APIs\n- Payment service calls not covered by the catalog\n- Enrichment APIs\n- Internal company APIs\n- Webhook delivery\n\nExternal API Steps\n/reference/steps/api\nConfigure HTTP calls in functions\nVariables & Data\n/flows/variables\nUse environment variables and step outputs\nAddons\n/addons\nUse catalog integrations when available"
  },
  {
    "path": "/addons/catalog/aws-s3",
    "title": "AWS S3",
    "description": "Upload, download, and manage files in Amazon S3 buckets from your Spala flows.",
    "section": "addons",
    "url": "https://docs.spala.ai/addons/catalog/aws-s3/",
    "last_updated": "2026-07-08",
    "content": "AWS S3\n\nThe AWS S3 addon provides file storage operations using Amazon Simple Storage Service. Upload files, generate signed URLs for secure downloads, list bucket contents, and delete objects — all from your Spala flows.\n\nInstallation\n\n1. Enable the AWS S3 addon from the Addons panel\n2. Enter your AWS credentials and bucket configuration\n\nConfiguration\n\nAvailable Steps\n\nUpload File\n\nUploads a file to S3.\n\nGet Signed URL\n\nGenerates a pre-signed URL for temporary secure access to a private file.\n\nDelete File\n\nDeletes an object from S3.\n\nList Files\n\nLists objects in an S3 bucket with an optional prefix filter.\n\nExample: File Upload Endpoint\n\n// POST /api/files/upload\n\n// Step 1: Set Variable - Generate a unique key\n// vars.key = 'uploads/' + vars.auth.userId + '/' + Date.now() + '-' + input.filename\n\n// Step 2: Upload File (S3 addon)\n// filePath: input.tempFilePath\n// key: vars.key\n// contentType: input.contentType\n\n// Step 3: Database Query - Save file record\n// INSERT INTO files (user_id, s3_key, filename, content_type, size)\n// VALUES (vars.auth.userId, vars.key, input.filename, input.contentType, input.fileSize)\n// RETURNING *\n\n// Step 4: Response\n// { file: vars.fileRecord }\n\nExample: Secure File Download\n\n// GET /api/files/:fileId/download\n\n// Step 1: Database Query - Get file record and verify ownership\n// SELECT * FROM files WHERE id = params.fileId AND user_id = vars.auth.userId\n\n// Step 2: Condition - File not found\n// If !vars.file → Respond with 404\n\n// Step 3: Get Signed URL (S3 addon)\n// key: vars.file.s3_key\n// expiresIn: 300\n\n// Step 4: Response\n// { downloadUrl: vars.signedUrl }\n\n  Pre-signed URLs provide temporary, secure access to private S3 objects without making the bucket public. Set the expiration time based on your use case — short for downloads (5 minutes), longer for media playback.\n\n  Use an IAM user with minimal S3 permissions. Grant access only to the specific bucket(s) your application needs, and avoid using root account credentials.\n\nAWS access key ID with S3 permissions.\nAWS secret access key.\nDefault S3 bucket name.\nAWS region (e.g., 'us-east-1').\nLocal path to the file to upload.\nThe S3 object key (path within the bucket, e.g., 'uploads/image.png').\nMIME type of the file (e.g., 'image/png'). Auto-detected if not provided.\nOverride the default bucket.\nThe S3 object key.\nURL expiration time in seconds (default: 3600).\nOverride the default bucket.\nThe S3 object key to delete.\nOverride the default bucket.\nFilter results to keys starting with this prefix (e.g., 'uploads/user-42/').\nMaximum number of results to return (default: 1000).\nOverride the default bucket.\nAddons Overview\n/addons\nBrowse available addons\nInstalling Addons\n/addons/installing\nEnable and configure addons in your project"
  },
  {
    "path": "/addons/creating-addons",
    "title": "Custom Integrations",
    "description": "Connect services that are not in the addon catalog using External API Call, webhooks, and project environment variables.",
    "section": "addons",
    "url": "https://docs.spala.ai/addons/creating-addons/",
    "last_updated": "2026-07-08",
    "content": "Custom Integrations\n\nMost integrations start with the addon catalog. If the service you need is not listed, use Spala's built-in API and webhook steps to connect it from your project.\n\nRecommended Options\n\n- External API Call — Call any HTTP API from a function, endpoint, task, or trigger\n- Webhooks — Receive or send events between Spala and another service\n- Project environment variables — Store API keys and secrets safely in Spala\n- AI Copilot — Describe the integration you want and let Spala create the tables, functions, and endpoints around it\n\nUse the dashboard first\n\n  Public Spala docs focus on using integrations from the dashboard. For private reusable integrations or organization-specific catalog items, contact Spala support.\n\nWhen To Contact Spala\n\nContact Spala support if you need a reusable organization-wide integration, a private catalog item, or help designing a complex workflow around a third-party service.\n\nAddons\n/addons\nBrowse ready-to-use integrations\nExternal API Steps\n/reference/steps/api\nCall any HTTP API from a function\nEnvironment Variables\n/settings\nStore API keys and runtime values\nAI Copilot\n/features/ai-assistant\nAsk Spala to build the integration workflow"
  },
  {
    "path": "/addons/catalog/discord",
    "title": "Discord",
    "description": "Send messages and notifications to Discord channels using webhooks from your Spala flows.",
    "section": "addons",
    "url": "https://docs.spala.ai/addons/catalog/discord/",
    "last_updated": "2026-07-08",
    "content": "Discord\n\nThe Discord addon lets you send messages to Discord channels through webhooks. Use it to post notifications, alerts, and updates to your Discord server directly from your Spala flows.\n\nInstallation\n\n1. Enable the Discord addon from the Addons panel\n2. Create a webhook in your Discord server (Server Settings > Integrations > Webhooks)\n3. Enter the webhook URL in the addon configuration\n\nConfiguration\n\n Integrations > Webhooks.' },\n\nAvailable Steps\n\nSend Message\n\nPosts a message to the Discord channel associated with the webhook.\n\nExample: Simple Notification\n\n// Event trigger: \"order.completed\"\n\n// Step 1: Send Message (Discord addon)\n// content: '**New Order!** Order #' + event.payload.orderId + ' for\n\nExample: Rich Embed Notification\n\n// Error handler flow\n\n// Step 1: Send Message (Discord addon)\n// username: 'Alert Bot'\n// embeds: [{\n//   title: 'Error Alert',\n//   description: vars.error.message,\n//   color: 16711680,\n//   fields: [\n//     { name: 'Flow', value: vars.flowName, inline: true },\n//     { name: 'Step', value: vars.stepName, inline: true },\n//     { name: 'Time', value: new Date().toISOString() }\n//   ],\n//   footer: { text: 'Spala Error Monitor' }\n// }]\n\nDiscord embeds support titles, descriptions, colored sidebars, fields, images, thumbnails, and footers. The color field accepts a decimal color value (e.g., 16711680 for red, 65280 for green).\n\n  Discord webhooks are channel-specific. To send messages to different channels, create separate webhooks and configure multiple environment variables, or pass the webhook URL as a step input.\n\n  Discord webhook URLs should be kept secret. Anyone with the URL can post messages to your channel. Store the URL in environment variables and never expose it in client-side code.\n\nThe full webhook URL from your Discord server. Found in Server Settings > Integrations > Webhooks.\nThe text content of the message. Supports Discord markdown.\nOverride the webhook display name for this message.\nArray of embed objects for rich message formatting.\nError Alert\nAddons Overview\n/addons\nBrowse available addons\nInstalling Addons\n/addons/installing\nEnable and configure addons in your project + event.payload.total.toFixed(2)\n\nExample: Rich Embed Notification\n\n// Error handler flow\n\n// Step 1: Send Message (Discord addon)\n// username: 'Alert Bot'\n// embeds: [{\n//   title: 'Error Alert',\n//   description: vars.error.message,\n//   color: 16711680,\n//   fields: [\n//     { name: 'Flow', value: vars.flowName, inline: true },\n//     { name: 'Step', value: vars.stepName, inline: true },\n//     { name: 'Time', value: new Date().toISOString() }\n//   ],\n//   footer: { text: 'Spala Error Monitor' }\n// }]\n\nDiscord embeds support titles, descriptions, colored sidebars, fields, images, thumbnails, and footers. The color field accepts a decimal color value (e.g., 16711680 for red, 65280 for green).\n\n  Discord webhooks are channel-specific. To send messages to different channels, create separate webhooks and configure multiple environment variables, or pass the webhook URL as a step input.\n\n  Discord webhook URLs should be kept secret. Anyone with the URL can post messages to your channel. Store the URL in environment variables and never expose it in client-side code.\n\nThe full webhook URL from your Discord server. Found in Server Settings > Integrations > Webhooks.\nThe text content of the message. Supports Discord markdown.\nOverride the webhook display name for this message.\nArray of embed objects for rich message formatting.\nError Alert\nAddons Overview\n/addons\nBrowse available addons\nInstalling Addons\n/addons/installing\nEnable and configure addons in your project"
  },
  {
    "path": "/addons/catalog/firebase-auth",
    "title": "Firebase Authentication",
    "description": "Verify Firebase ID tokens and manage users with the Firebase Auth addon for Spala.",
    "section": "addons",
    "url": "https://docs.spala.ai/addons/catalog/firebase-auth/",
    "last_updated": "2026-07-08",
    "content": "Firebase Authentication\n\nThe Firebase Authentication addon lets you verify Firebase ID tokens in your Spala flows. Use it to authenticate users who sign in through Firebase on your frontend (Google, Apple, email/password, phone number) and access their user information in your backend flows.\n\nInstallation\n\n1. Enable the Firebase Auth addon from the Addons panel\n2. Enter your Firebase project credentials in the addon configuration\n\nConfiguration\n\n Project Settings.' },\n\nAvailable Steps\n\nVerify ID Token\n\nVerifies a Firebase ID token and returns the decoded user information.\n\nReturns the decoded token payload:\n\n{\n  uid: \"abc123\",\n  email: \"user@example.com\",\n  name: \"Jane Smith\",\n  picture: \"https://...\",\n  email_verified: true,\n  auth_time: 1705000000\n}\n\nGet User\n\nRetrieves a Firebase user record by UID.\n\nExample: Protected Endpoint with Firebase Auth\n\n// GET /api/profile (middleware or first step)\n\n// Step 1: Set Variable - Extract token from header\n// vars.token = headers.authorization?.replace('Bearer ', '')\n\n// Step 2: Condition - Check if token exists\n// If !vars.token → Respond with 401\n\n// Step 3: Verify ID Token (Firebase Auth addon)\n// idToken: vars.token\n// Output → vars.firebaseUser\n\n// Step 4: Database Query - Get user profile\n// SELECT * FROM users WHERE firebase_uid = vars.firebaseUser.uid\n\n// Step 5: Response\n// { user: vars.profile }\n\nExample: Sync Firebase Users to Database\n\n// POST /api/auth/sync\n\n// Step 1: Verify ID Token (Firebase Auth addon)\n// idToken: input.idToken\n// Output → vars.firebaseUser\n\n// Step 2: Database Query - Upsert user\n// INSERT INTO users (firebase_uid, email, name, avatar_url)\n// VALUES (vars.firebaseUser.uid, vars.firebaseUser.email, vars.firebaseUser.name, vars.firebaseUser.picture)\n// ON CONFLICT (firebase_uid) DO UPDATE SET\n//   email = EXCLUDED.email, name = EXCLUDED.name, avatar_url = EXCLUDED.avatar_url,\n//   last_login = NOW()\n// RETURNING *\n\n// Step 3: Response\n// { user: vars.user }\n\n  Firebase ID tokens are short-lived (1 hour). Your frontend should handle token refresh automatically using the Firebase SDK, and send the fresh token with each API request.\n\n  Always verify the ID token on the server side. Never trust user information sent directly from the client without token verification.\n\nYour Firebase project ID from the Firebase Console > Project Settings.\nYour Firebase Web API key from the Firebase Console > Project Settings.\nThe Firebase ID token to verify (typically sent in the Authorization header from the frontend).\nThe Firebase user UID.\nAddons Overview\n/addons\nBrowse available addons\nInstalling Addons\n/addons/installing\nEnable and configure addons in your project"
  },
  {
    "path": "/addons/catalog/github",
    "title": "GitHub",
    "description": "Interact with GitHub repositories, issues, and pull requests from your Spala flows.",
    "section": "addons",
    "url": "https://docs.spala.ai/addons/catalog/github/",
    "last_updated": "2026-07-08",
    "content": "GitHub\n\nThe GitHub addon connects your Spala flows to the GitHub API. Create issues, manage pull requests, query repositories, and build automations around your development workflow.\n\nInstallation\n\n1. Enable the GitHub addon from the Addons panel\n2. Create a personal access token or GitHub App token\n3. Enter the token in the addon configuration\n\nConfiguration\n\nAvailable Steps\n\nCreate Issue\n\nCreates a new issue in a GitHub repository.\n\nList Issues\n\nRetrieves issues from a repository.\n\nCreate Comment\n\nAdds a comment to an issue or pull request.\n\nExample: Auto-Create Issue from Bug Report\n\n// POST /api/bug-reports\n\n// Step 1: Create Issue (GitHub addon)\n// owner: 'my-org'\n// repo: 'my-app'\n// title: '[Bug] ' + input.title\n// body: '**Reported by:** ' + vars.auth.email + '\\n\\n' + input.description + '\\n\\n**Steps to reproduce:**\\n' + input.steps\n// labels: ['bug', 'user-reported']\n// Output → vars.issue\n\n// Step 2: Database Query - Link report to issue\n// INSERT INTO bug_reports (user_id, title, github_issue_number, github_issue_url)\n// VALUES (vars.auth.userId, input.title, vars.issue.number, vars.issue.html_url)\n\n// Step 3: Response\n// { issueUrl: vars.issue.html_url }\n\nExample: Deployment Tracker\n\n// Event trigger: \"deployment.completed\"\n\n// Step 1: Create Comment (GitHub addon)\n// owner: 'my-org'\n// repo: 'my-app'\n// issueNumber: event.payload.prNumber\n// body: '**Deployed to production** :rocket:\\nVersion: ' + event.payload.version + '\\nTime: ' + new Date().toISOString()\n\n  For personal access tokens, use fine-grained tokens with the minimum required permissions. For issue management, you need \"Issues\" read/write access for the specific repository.\n\nGitHub personal access token or GitHub App installation token with the required scopes.\nRepository owner (user or organization).\nRepository name.\nIssue title.\nIssue body in Markdown format.\nArray of label names to apply.\nArray of GitHub usernames to assign.\nRepository owner.\nRepository name.\n'open', 'closed', or 'all' (default: 'open').\nComma-separated list of label names to filter by.\nRepository owner.\nRepository name.\nThe issue or pull request number.\nComment body in Markdown.\n[Bug]\nAddons Overview\n/addons\nBrowse available addons\nInstalling Addons\n/addons/installing\nEnable and configure addons in your project"
  },
  {
    "path": "/addons/catalog/html-to-pdf",
    "title": "HTML to PDF",
    "description": "Generate PDF documents from HTML templates in your Spala flows.",
    "section": "addons",
    "url": "https://docs.spala.ai/addons/catalog/html-to-pdf/",
    "last_updated": "2026-07-08",
    "content": "HTML to PDF\n\nThe HTML to PDF addon generates PDF documents from HTML content using a headless browser engine. Create invoices, reports, receipts, certificates, and any other document by writing HTML templates and converting them to PDF.\n\nInstallation\n\n1. Enable the HTML to PDF addon from the Addons panel\n2. No setup beyond enabling the addon\n\nNo API keys or external services are required — all rendering happens locally on the server.\n\nAvailable Steps\n\nGenerate PDF\n\nRenders HTML content as a PDF document.\n\nGenerate PDF from URL\n\nRenders a web page at the given URL as a PDF.\n\nExample: Invoice Generation\n\n// POST /api/invoices/:orderId/pdf\n\n// Step 1: Database Query - Get order with items\n// SELECT o.*, json_agg(oi.*) as items FROM orders o\n// JOIN order_items oi ON oi.order_id = o.id\n// WHERE o.id = params.orderId GROUP BY o.id\n\n// Step 2: Set Variable - Build HTML\n// vars.html = `\n//   <html>\n//   <style>\n//     body { font-family: Arial, sans-serif; padding: 40px; }\n//     .header { display: flex; justify-content: space-between; }\n//     table { width: 100%; border-collapse: collapse; margin-top: 20px; }\n//     th, td { border: 1px solid #ddd; padding: 8px; text-align: left; }\n//     .total { font-size: 24px; text-align: right; margin-top: 20px; }\n//   </style>\n//   <body>\n//     <div class=\"header\n//       <h1>Invoice #${vars.order.id}</h1>\n//       <p>Date: ${new Date(vars.order.created_at).toLocaleDateString()}</p>\n//     </div>\n//     <table>\n//       <tr><th>Item</th><th>Qty</th><th>Price</th><th>Total</th></tr>\n//       ${vars.order.items.map(item => `\n//         <tr>\n//           <td>${item.name}</td>\n//           <td>${item.quantity}</td>\n//           <td>${item.price.toFixed(2)}</td>\n//           <td>${(item.price * item.quantity).toFixed(2)}</td>\n//         </tr>\n//       `).join('')}\n//     </table>\n//     <p class=\"total\">Total: ${vars.order.total.toFixed(2)}</p>\n//   </body>\n//   </html>\n// `\n\n// Step 3: Generate PDF (HTML to PDF addon)\n// html: vars.html\n// outputPath: storage + '/invoices/invoice-' + vars.order.id + '.pdf'\n// format: 'A4'\n// margin: { top: '20mm', bottom: '20mm', left: '15mm', right: '15mm' }\n\n// Step 4: Response\n// { pdfPath: vars.pdfPath }\n\nExample: Report with Header/Footer\n\n// Step 1: Generate PDF (HTML to PDF addon)\n// html: vars.reportHtml\n// outputPath: storage + '/reports/monthly-' + vars.month + '.pdf'\n// format: 'A4'\n// headerTemplate: '<div style=\"font-size:10px; width:100%; text-align:center;\">Monthly Report - <span class=\"date\"></span></div>'\n// footerTemplate: '<div style=\"font-size:10px; width:100%; text-align:center;\">Page <span class=\"pageNumber\"></span> of <span class=\"totalPages\"></span></div>'\n// margin: { top: '30mm', bottom: '20mm' }\n\n  The HTML template supports full CSS including Flexbox and Grid layouts. You can use inline images with base64 data URIs or reference external image URLs.\n\n  PDF generation requires a headless browser and is resource-intensive. For high-volume document generation, run the operation in a background task to avoid blocking API responses.\n\nThe HTML content to render. Can include inline CSS and images.\nFile path where the PDF will be saved.\nPaper size: 'A4' (default), 'Letter', 'Legal', 'A3', 'A5', or 'Tabloid'.\nSet to true for landscape orientation (default: false).\nMargins object with top, right, bottom, left values (e.g., { top: '20mm', bottom: '20mm' }).\nHTML template for the page header. Use CSS classes: date, title, url, pageNumber, totalPages.\nHTML template for the page footer.\nThe URL of the page to render.\nFile path where the PDF will be saved.\nPaper size (default: A4).\nCSS selector to wait for before rendering (useful for pages with async content).\nAddons Overview\n/addons\nBrowse available addons\nInstalling Addons\n/addons/installing\nEnable and configure addons in your project"
  },
  {
    "path": "/addons/catalog/hubspot",
    "title": "HubSpot",
    "description": "Manage contacts, deals, and CRM data through the HubSpot API from your Spala flows.",
    "section": "addons",
    "url": "https://docs.spala.ai/addons/catalog/hubspot/",
    "last_updated": "2026-07-08",
    "content": "HubSpot\n\nThe HubSpot addon connects your Spala flows to the HubSpot CRM. Create and update contacts, manage deals, and sync customer data between your application and HubSpot.\n\nInstallation\n\n1. Enable the HubSpot addon from the Addons panel\n2. Create a private app in HubSpot (Settings > Integrations > Private Apps)\n3. Enter the access token in the addon configuration\n\nConfiguration\n\n Integrations > Private Apps.' },\n\nAvailable Steps\n\nCreate Contact\n\nCreates a new contact in HubSpot.\n\nUpdate Contact\n\nUpdates an existing HubSpot contact.\n\nCreate Deal\n\nCreates a new deal in the HubSpot sales pipeline.\n\nSearch Contacts\n\nSearches for contacts matching specified criteria.\n\nExample: Sync New Users to HubSpot\n\n// Event trigger: \"user.registered\"\n\n// Step 1: Create Contact (HubSpot addon)\n// email: event.payload.email\n// firstName: event.payload.firstName\n// lastName: event.payload.lastName\n// properties: {\n//   company: event.payload.company,\n//   signup_source: 'web_app',\n//   plan: 'free'\n// }\n// Output → vars.hubspotContact\n\n// Step 2: Database Query - Store HubSpot ID\n// UPDATE users SET hubspot_contact_id = vars.hubspotContact.id\n// WHERE id = event.payload.userId\n\nExample: Create Deal on Purchase\n\n// Database trigger on insert for the \"orders\" table\n\n// Step 1: Database Query - Get customer HubSpot ID\n// SELECT hubspot_contact_id FROM users WHERE id = trigger.newRow.user_id\n\n// Step 2: Create Deal (HubSpot addon)\n// dealName: 'Order #' + trigger.newRow.id\n// amount: trigger.newRow.total\n// stage: 'closedwon'\n// properties: { order_id: trigger.newRow.id.toString() }\n\n  HubSpot private apps require specific scopes for different operations. Grant crm.objects.contacts.write for contact operations and crm.objects.deals.write for deal operations.\n\nPrivate app access token from HubSpot. Found in Settings > Integrations > Private Apps.\nContact email address.\nContact first name.\nContact last name.\nAdditional contact properties as key-value pairs.\nThe HubSpot contact ID.\nProperties to update as key-value pairs.\nName of the deal.\nDeal amount.\nPipeline stage (e.g., \"qualifiedtobuy\", \"closedwon\").\nAdditional deal properties.\nSearch query string.\nArray of filter objects with property, operator, and value.\nMaximum results to return (default: 10).\nAddons Overview\n/addons\nBrowse available addons\nInstalling Addons\n/addons/installing\nEnable and configure addons in your project"
  },
  {
    "path": "/addons/catalog/image-processing",
    "title": "Image Processing",
    "description": "Resize, crop, convert, and transform images using Sharp from your Spala flows.",
    "section": "addons",
    "url": "https://docs.spala.ai/addons/catalog/image-processing/",
    "last_updated": "2026-07-08",
    "content": "Image Processing\n\nThe Image Processing addon uses the Sharp library to resize, crop, convert, and transform images in your Spala flows. It runs image operations on the server with high performance, supporting JPEG, PNG, WebP, AVIF, GIF, and TIFF formats.\n\nInstallation\n\n1. Enable the Image Processing addon from the Addons panel\n2. No setup beyond enabling the addon\n\nNo API keys or external services are required — all processing happens locally on the server.\n\nAvailable Steps\n\nResize Image\n\nResizes an image to the specified dimensions.\n\nConvert Format\n\nConverts an image to a different format.\n\nCrop Image\n\nExtracts a region from an image.\n\nGet Image Info\n\nReturns metadata about an image (dimensions, format, file size, color space).\n\nReturns:\n\n{\n  width: 1920,\n  height: 1080,\n  format: \"jpeg\",\n  size: 245760,\n  channels: 3,\n  space: \"srgb\"\n}\n\nExample: Image Upload with Thumbnails\n\n// POST /api/images/upload\n\n// Step 1: Save File (Local Storage addon)\n// path: 'uploads/originals/' + input.filename\n// content: input.fileData, encoding: 'base64'\n\n// Step 2: Resize Image (Image Processing addon)\n// inputPath: storage + '/uploads/originals/' + input.filename\n// outputPath: storage + '/uploads/thumbnails/' + input.filename\n// width: 200\n// height: 200\n// fit: 'cover'\n\n// Step 3: Convert Format (Image Processing addon)\n// inputPath: storage + '/uploads/originals/' + input.filename\n// outputPath: storage + '/uploads/webp/' + input.filename.replace(/\\.[^.]+$/, '.webp')\n// format: 'webp'\n// quality: 85\n\n// Step 4: Database Query - Save image record\n// INSERT INTO images (original_path, thumbnail_path, webp_path, ...)\n\n// Step 5: Response\n// { image: vars.imageRecord }\n\nExample: Avatar Processing\n\n// POST /api/users/avatar\n\n// Step 1: Save uploaded file temporarily\n\n// Step 2: Get Image Info (Image Processing addon)\n// inputPath: vars.tempPath\n// Output → vars.info\n\n// Step 3: Condition - Validate dimensions\n// If vars.info.width < 100 || vars.info.height < 100\n//   Respond with 400: \"Image must be at least 100x100 pixels\"\n\n// Step 4: Resize Image (Image Processing addon)\n// inputPath: vars.tempPath\n// outputPath: storage + '/avatars/' + vars.auth.userId + '.webp'\n// width: 256, height: 256, fit: 'cover'\n\n// Step 5: Convert Format\n// format: 'webp', quality: 90\n\n  Sharp processes images in a streaming pipeline, which is memory-efficient even for large images. It uses the libvips library under the hood, which is significantly faster than ImageMagick or GraphicsMagick.\n\n  Image processing is CPU-intensive. For high-volume image processing, consider running these operations in background tasks to avoid blocking your API endpoints.\n\nPath to the source image file.\nPath where the resized image will be saved.\nTarget width in pixels. Omit to auto-calculate from height.\nTarget height in pixels. Omit to auto-calculate from width.\n'cover' (crop to fill), 'contain' (fit within), 'fill' (stretch), 'inside', or 'outside'.\nPath to the source image file.\nPath for the converted image.\n'jpeg', 'png', 'webp', 'avif', 'gif', or 'tiff'.\nOutput quality for lossy formats, 1-100 (default: 80).\nPath to the source image.\nPath for the cropped image.\nLeft offset in pixels.\nTop offset in pixels.\nWidth of the crop region.\nHeight of the crop region.\nPath to the image file.\nAddons Overview\n/addons\nBrowse available addons\nInstalling Addons\n/addons/installing\nEnable and configure addons in your project"
  },
  {
    "path": "/addons/installing",
    "title": "Installing Addons",
    "description": "How to enable and configure addons in your Spala project.",
    "section": "addons",
    "url": "https://docs.spala.ai/addons/installing/",
    "last_updated": "2026-07-08",
    "content": "Installing Addons\n\nAddons are bundled with Spala but disabled by default. To use an addon, you need to enable it in your project and configure any required API keys or credentials.\n\nEnabling an Addon\n\nOpen your project in Spala\n\nClick the Addons icon in the left sidebar\n\nBrowse or search the addon catalog to find the addon you need\n\nClick on the addon to view its details, available steps, and required configuration\n\nClick Enable to activate the addon for your project\n\nIf the addon requires API keys or other credentials, enter them in the configuration panel\n\nClick Save to complete the installation\n\nOnce enabled, the addon's steps appear in the flow editor's step palette. You can drag them into any flow just like built-in steps.\n\nConfiguring API Keys\n\nMost addons that connect to external services require API keys or other credentials. These are stored as project environment variables in Spala and are never exposed to the frontend.\n\nTo configure an addon's credentials:\n\n1. Open the addon's settings from the Addons panel\n2. Enter the required values (API keys, secrets, webhook URLs, etc.)\n3. Click Save\n\nThe configuration panel shows which environment variables each addon needs, along with links to the external service's documentation for obtaining API keys.\n\n# Example environment variables for common addons\nSTRIPE_SECRET_KEY=sk_live_...\nOPENAI_API_KEY=sk-...\nTWILIO_ACCOUNT_SID=AC...\nTWILIO_AUTH_TOKEN=...\nSENDGRID_API_KEY=SG...\n\n  Never commit API keys to version control or share them in client-side code. Store secrets in Spala project environment variables or addon settings.\n\nVerifying Installation\n\nAfter enabling an addon, verify it is working:\n\n1. Open the flow editor for any endpoint or task\n2. Open the step palette and search for the addon's step types\n3. Drag a step into the flow and configure it\n4. Use the Playground to test the endpoint\n\nIf the addon's steps do not appear in the palette, make sure you saved the addon configuration and refreshed the page.\n\nDisabling an Addon\n\nTo disable an addon you no longer need:\n\n1. Open the Addons panel\n2. Find the enabled addon\n3. Click Disable\n\nDisabling an addon removes its steps from the palette. Any flows that use the addon's steps will show a warning, and those steps will fail at runtime. Remove or replace the addon steps from your flows before disabling the addon to avoid errors.\n\n  Disabling an addon does not delete its configuration. If you re-enable the addon later, your API keys and settings are preserved.\n\nAddons Overview\n/addons\nBrowse available addons\nExternal API Steps\n/reference/steps/api\nCall services that are not in the addon catalog"
  },
  {
    "path": "/addons/addon-manifest",
    "title": "Integration Configuration",
    "description": "Configure addon credentials and integration settings from the Spala dashboard.",
    "section": "addons",
    "url": "https://docs.spala.ai/addons/addon-manifest/",
    "last_updated": "2026-07-08",
    "content": "Integration Configuration\n\nCatalog addons show their required configuration in the Spala dashboard. When an addon needs an API key, webhook secret, account ID, or region, Spala prompts you for the value during setup.\n\nWhere To Configure Values\n\n- Addon settings — Values specific to the enabled addon\n- Settings > Environment — Project-level secrets and runtime values\n- Endpoint/function inputs — Values that should vary per request\n\nProtect secrets\n\n  Store API keys and webhook secrets in Spala project settings. Do not expose them in browser code or public repositories.\n\nCustom Service Calls\n\nIf the integration is not in the catalog, use the External API Call step and reference credentials from project environment variables.\n\nInstalling Addons\n/addons/installing\nEnable and configure catalog addons\nExternal API Steps\n/reference/steps/api\nCall services directly from functions\nSettings\n/settings\nManage environment variables and project controls"
  },
  {
    "path": "/addons/catalog/jira",
    "title": "Jira",
    "description": "Create and manage Jira issues and projects from your Spala flows.",
    "section": "addons",
    "url": "https://docs.spala.ai/addons/catalog/jira/",
    "last_updated": "2026-07-08",
    "content": "Jira\n\nThe Jira addon integrates Atlassian Jira into your Spala flows. Create issues, update statuses, search with JQL, and automate your project management workflows.\n\nInstallation\n\n1. Enable the Jira addon from the Addons panel\n2. Create an API token at id.atlassian.com/manage-profile/security/api-tokens\n3. Enter your Jira credentials in the addon configuration\n\nConfiguration\n\nAvailable Steps\n\nCreate Issue\n\nCreates a new Jira issue.\n\nUpdate Issue\n\nUpdates fields on an existing Jira issue.\n\nSearch Issues (JQL)\n\nSearches for issues using Jira Query Language.\n\nTransition Issue\n\nChanges the status of a Jira issue (e.g., move from \"To Do\" to \"In Progress\").\n\nExample: Auto-Create Bug from Error\n\n// Error handler flow\n\n// Step 1: Create Issue (Jira addon)\n// projectKey: 'APP'\n// issueType: 'Bug'\n// summary: '[Auto] Error in ' + vars.flowName + ': ' + vars.error.message\n// description: 'Automatically created from error monitoring.\\n\\nFlow: ' + vars.flowName + '\\nStep: ' + vars.stepName + '\\nTime: ' + new Date().toISOString() + '\\n\\nStack trace:\\n' + vars.error.stack\n// priority: 'High'\n// Output → vars.jiraIssue\n\n// Step 2: Log\n// 'Created Jira issue: ' + vars.jiraIssue.key\n\nExample: Sprint Report\n\n// GET /api/sprint-report\n\n// Step 1: Search Issues (Jira addon)\n// jql: 'project = APP AND sprint in openSprints() AND status = Done'\n// fields: ['summary', 'assignee', 'status', 'story_points']\n// Output → vars.completedIssues\n\n// Step 2: Response\n// { completed: vars.completedIssues.total, issues: vars.completedIssues.issues }\n\n  Jira API authentication uses Basic Auth with your email and API token. The addon handles the encoding automatically. Make sure the account associated with the token has the necessary project permissions.\n\nYour Jira instance URL (e.g., \"https://yourteam.atlassian.net\").\nYour Atlassian account email address.\nAPI token from your Atlassian account settings.\nThe project key (e.g., 'PROJ').\nIssue type: 'Bug', 'Task', 'Story', 'Epic', etc.\nIssue title/summary.\nIssue description (supports Atlassian Document Format or plain text).\nPriority: 'Highest', 'High', 'Medium', 'Low', 'Lowest'.\nAtlassian account ID of the assignee.\nThe issue key (e.g., 'PROJ-123').\nObject of fields to update.\nJQL query string (e.g., \"project = PROJ AND status = Open\").\nMaximum results to return (default: 50).\nArray of field names to include in results.\nThe issue key.\nThe transition ID (get available transitions from the Jira API).\nAutomatically created from error monitoring.\\n\\nFlow:\nAddons Overview\n/addons\nBrowse available addons\nInstalling Addons\n/addons/installing\nEnable and configure addons in your project"
  },
  {
    "path": "/addons/catalog/local-storage",
    "title": "Local Storage",
    "description": "Read and write files on the server's local file system from your Spala flows.",
    "section": "addons",
    "url": "https://docs.spala.ai/addons/catalog/local-storage/",
    "last_updated": "2026-07-08",
    "content": "Local Storage\n\nThe Local Storage addon provides managed file operations for reading, writing, and organizing project files. Use it for uploads, temporary file processing, export generation, and workflows that need file storage.\n\nInstallation\n\n1. Enable the Local Storage addon from the Addons panel\n2. Optionally configure the base storage directory\n\nConfiguration\n\nAvailable Steps\n\nSave File\n\nWrites content to a file on the local file system.\n\nRead File\n\nReads the contents of a file.\n\nDelete File\n\nDeletes a file from the file system.\n\nList Files\n\nLists files in a directory.\n\nFile Exists\n\nChecks whether a file exists at the given path.\n\nExample: File Upload Endpoint\n\n// POST /api/files/upload\n\n// Step 1: Set Variable - Generate unique filename\n// vars.filename = Date.now() + '-' + input.originalName\n\n// Step 2: Save File (Local Storage addon)\n// path: 'uploads/' + vars.auth.userId + '/' + vars.filename\n// content: input.fileData\n// encoding: 'base64'\n\n// Step 3: Database Query - Save file record\n// INSERT INTO files (user_id, filename, path, size, mime_type)\n// VALUES (vars.auth.userId, input.originalName, 'uploads/' + vars.auth.userId + '/' + vars.filename, input.fileSize, input.mimeType)\n// RETURNING *\n\n// Step 4: Response\n// { file: vars.fileRecord }\n\nExample: Export to CSV\n\n// GET /api/reports/export\n\n// Step 1: Database Query - Get report data\n// SELECT * FROM orders WHERE created_at >= input.startDate\n\n// Step 2: Set Variable - Build CSV content\n// vars.csv = 'id,customer,total,status,date\\n' +\n//   vars.orders.map(o => [o.id, o.customer_name, o.total, o.status, o.created_at].join(',')).join('\\n')\n\n// Step 3: Save File (Local Storage addon)\n// path: 'exports/orders-' + Date.now() + '.csv'\n// content: vars.csv\n// encoding: 'utf8'\n// Output → vars.filePath\n\n// Step 4: Response\n// { downloadPath: vars.filePath }\n\n  All file paths are sandboxed to the configured storage directory. Attempting to access files outside this directory (e.g., using \"../\" path traversal) will be blocked for security.\n\n  For production deployments with multiple server instances, consider using the AWS S3 addon instead of local storage. Local storage is tied to a single server and files will not be available across instances.\n\nBase directory for file operations (default: './storage' relative to the project root). All file paths are resolved relative to this directory.\nFile path relative to the storage directory (e.g., 'uploads/report.pdf').\nFile content — a string, Buffer, or base64-encoded data.\n'utf8' (default), 'base64', 'binary', or 'buffer'.\nFile path relative to the storage directory.\n'utf8' (default), 'base64', 'binary', or 'buffer'.\nFile path relative to the storage directory.\nDirectory path relative to the storage directory (default: root '/').\nWhether to list files in subdirectories (default: false).\nFile path to check.\nAddons Overview\n/addons\nBrowse available addons\nInstalling Addons\n/addons/installing\nEnable and configure addons in your project"
  },
  {
    "path": "/addons/catalog/notion",
    "title": "Notion",
    "description": "Create pages, query databases, and manage content in Notion from your Spala flows.",
    "section": "addons",
    "url": "https://docs.spala.ai/addons/catalog/notion/",
    "last_updated": "2026-07-08",
    "content": "Notion\n\nThe Notion addon connects your Spala flows to Notion's API. Query databases, create pages, update properties, and sync data between your application and Notion workspaces.\n\nInstallation\n\n1. Enable the Notion addon from the Addons panel\n2. Create an integration at notion.so/my-integrations\n3. Share the relevant Notion databases/pages with your integration\n4. Enter the integration token in the addon configuration\n\nConfiguration\n\nAvailable Steps\n\nQuery Database\n\nQueries a Notion database with optional filters and sorting.\n\nCreate Page\n\nCreates a new page in a Notion database.\n\nUpdate Page\n\nUpdates properties on an existing Notion page.\n\nExample: Log Errors to Notion\n\n// Error handler in a flow\n\n// Step 1: Create Page (Notion addon)\n// databaseId: env.NOTION_ERROR_DB_ID\n// properties: {\n//   \"Error\": { title: [{ text: { content: vars.error.message } }] },\n//   \"Flow\": { rich_text: [{ text: { content: vars.flowName } }] },\n//   \"Status\": { select: { name: \"Open\" } },\n//   \"Severity\": { select: { name: \"High\" } },\n//   \"Time\": { date: { start: new Date().toISOString() } }\n// }\n\nExample: Sync Tasks from Notion\n\n// Scheduled task — runs every 15 minutes\n\n// Step 1: Query Database (Notion addon)\n// databaseId: env.NOTION_TASKS_DB_ID\n// filter: { property: \"Status\", select: { equals: \"Ready\" } }\n// Output → vars.tasks\n\n// Step 2: Loop - For each task\n//   Step 3: Database Query - Create local task\n//   INSERT INTO tasks (notion_id, title, priority, due_date)\n//   VALUES (task.id, task.properties.Name.title[0].plain_text,\n//           task.properties.Priority.select.name,\n//           task.properties.Due.date?.start)\n//   ON CONFLICT (notion_id) DO UPDATE SET ...\n\n  Notion databases must be explicitly shared with your integration before they can be accessed. In Notion, open the database page, click the three-dot menu, and select \"Connect to\" to add your integration.\n\n  Notion's property format is verbose. Each property type (title, rich_text, select, date, etc.) has its own structure. Refer to the Notion API documentation for the exact format of each property type.\n\nInternal integration token from notion.so/my-integrations.\nThe Notion database ID.\nFilter object following the Notion filter syntax.\nArray of sort objects (e.g., [{ property: \"Created\", direction: \"descending\" }]).\nNumber of results per page (default: 100, max: 100).\nThe parent database ID.\nPage properties matching the database schema.\nArray of block objects for the page input.\nThe Notion page ID.\nProperties to update.\nAddons Overview\n/addons\nBrowse available addons\nInstalling Addons\n/addons/installing\nEnable and configure addons in your project"
  },
  {
    "path": "/addons/catalog/openai",
    "title": "OpenAI",
    "description": "Integrate OpenAI's GPT models, embeddings, and image generation into your Spala flows.",
    "section": "addons",
    "url": "https://docs.spala.ai/addons/catalog/openai/",
    "last_updated": "2026-07-08",
    "content": "OpenAI\n\nThe OpenAI addon connects your Spala flows to OpenAI's API for chat completions, text embeddings, and image generation. Build AI-powered features like chatbots, content generation, semantic search, and automated analysis.\n\nInstallation\n\n1. Enable the OpenAI addon from the Addons panel\n2. Enter your OpenAI API key in the addon configuration\n\nConfiguration\n\nAvailable Steps\n\nChat Completion\n\nGenerates a chat response from an OpenAI model.\n\n// Example messages input:\n[\n  { role: 'system', content: 'You are a helpful customer support agent.' },\n  { role: 'user', content: input.userMessage }\n]\n\nCreate Embedding\n\nGenerates vector embeddings for text, useful for semantic search and similarity matching.\n\nGenerate Image\n\nCreates images from text descriptions using an OpenAI image model.\n\nExample: AI Chatbot Endpoint\n\n// POST /api/chat\n\n// Step 1: Database Query - Get conversation history\n// SELECT * FROM messages WHERE conversation_id = input.conversationId\n// ORDER BY created_at ASC LIMIT 20\n\n// Step 2: Chat Completion (OpenAI addon)\n// model: '<current-openai-model>'\n// messages: [\n//   { role: 'system', content: 'You are a helpful assistant for our e-commerce store.' },\n//   ...vars.history.map(m => ({ role: m.role, content: m.content })),\n//   { role: 'user', content: input.message }\n// ]\n// Output → vars.aiResponse\n\n// Step 3: Database Query - Save the messages\n// INSERT INTO messages (conversation_id, role, content)\n// VALUES (input.conversationId, 'user', input.message),\n//        (input.conversationId, 'assistant', vars.aiResponse.choices[0].message.content)\n\n// Step 4: Response\n// { reply: vars.aiResponse.choices[0].message.content }\n\nExample: Semantic Search\n\n// POST /api/search\n\n// Step 1: Create Embedding (OpenAI addon)\n// model: 'text-embedding-3-small'\n// input: input.query\n// Output → vars.embedding\n\n// Step 2: Database Query - Find similar documents\n// SELECT *, (embedding <=> $1) AS distance\n// FROM documents ORDER BY distance LIMIT 10\n// Params: [vars.embedding.data[0].embedding]\n\n// Step 3: Response\n// { results: vars.documents }\n\n  OpenAI API calls are billed per token. Monitor your usage in the OpenAI dashboard and set billing limits to avoid unexpected charges.\n\nYour OpenAI API key from platform.openai.com/api-keys.\nThe OpenAI model to use. Choose a currently supported model from OpenAI.\nArray of message objects with 'role' and 'content' fields.\nSampling temperature between 0 and 2 (default: 1). Lower values are more deterministic.\nMaximum number of tokens to generate.\nThe embedding model (e.g., 'text-embedding-3-small').\nThe text string to embed, or an array of strings for batch embedding.\nA text description of the image to generate.\nThe OpenAI image model to use.\nImage size: '1024x1024', '1792x1024', or '1024x1792'.\n'standard' or 'hd' (default: 'standard').\nAddons Overview\n/addons\nBrowse available addons\nInstalling Addons\n/addons/installing\nEnable and configure addons in your project"
  },
  {
    "path": "/addons/catalog/qr-code",
    "title": "QR Code",
    "description": "Generate QR codes as images or data URIs from your Spala flows.",
    "section": "addons",
    "url": "https://docs.spala.ai/addons/catalog/qr-code/",
    "last_updated": "2026-07-08",
    "content": "QR Code\n\nThe QR Code addon generates QR code images from text, URLs, or any string data. Use it for generating shareable links, event tickets, product labels, two-factor authentication setup, and payment links.\n\nInstallation\n\n1. Enable the QR Code addon from the Addons panel\n\nNo API keys are required. QR codes are generated through the addon; do not send sensitive secrets in QR payloads unless you have confirmed the data handling requirements for your project.\n\nAvailable Steps\n\nGenerate QR Code\n\nCreates a QR code image from the provided data.\n\nReturns an object with the QR code image data:\n\n{\n  imageUrl: \"https://api.qrserver.com/v1/create-qr-code/?data=...\",\n  dataUri: \"data:image/png;base64,iVBORw0KGgo...\",\n  data: \"https://myapp.com/ticket/abc123\"\n}\n\nExample: Event Ticket QR Code\n\n// POST /api/tickets/generate\n\n// Step 1: Database Query - Create ticket record\n// INSERT INTO tickets (event_id, user_id, code)\n// VALUES (input.eventId, vars.auth.userId, gen_random_uuid())\n// RETURNING *\n\n// Step 2: Generate QR Code (QR Code addon)\n// data: 'https://myapp.com/tickets/verify/' + vars.ticket.code\n// size: 300\n// Output → vars.qrCode\n\n// Step 3: Database Query - Store QR code URL\n// UPDATE tickets SET qr_code_url = vars.qrCode.imageUrl WHERE id = vars.ticket.id\n\n// Step 4: Response\n// { ticket: vars.ticket, qrCode: vars.qrCode.imageUrl }\n\nExample: Two-Factor Authentication Setup\n\n// POST /api/auth/2fa/setup\n\n// Step 1: Set Variable - Generate TOTP secret\n// vars.secret = generateTOTPSecret()\n\n// Step 2: Set Variable - Build otpauth URI\n// vars.otpauthUri = 'otpauth://totp/MyApp:' + vars.auth.email\n//   + '?secret=' + vars.secret + '&issuer=MyApp'\n\n// Step 3: Generate QR Code (QR Code addon)\n// data: vars.otpauthUri\n// size: 250\n// Output → vars.qrCode\n\n// Step 4: Database Query - Store secret (encrypted)\n// UPDATE users SET totp_secret = vars.secret WHERE id = vars.auth.userId\n\n// Step 5: Response\n// { qrCodeDataUri: vars.qrCode.dataUri, secret: vars.secret }\n\nExample: Product Label\n\n// GET /api/products/:id/qr\n\n// Step 1: Database Query - Get product\n// SELECT * FROM products WHERE id = params.id\n\n// Step 2: Generate QR Code (QR Code addon)\n// data: 'https://mystore.com/products/' + vars.product.slug\n// size: 400\n// darkColor: '1a1a2e'\n// lightColor: 'ffffff'\n// Output → vars.qrCode\n\n// Step 3: Response\n// { productName: vars.product.name, qrCodeUrl: vars.qrCode.imageUrl }\n\n  QR codes can encode up to about 4,000 alphanumeric characters. For longer data, consider encoding a short URL that redirects to the full content.\n\n  The dataUri format embeds the entire image as a base64 string, which is useful for embedding in HTML or emails but increases the response payload size. Use imageUrl when you only need a URL reference.\n\nThe text or URL to encode in the QR code.\nImage size in pixels (width and height). Default: 200.\n'png' (default) or 'svg'.\nQuiet zone margin in modules (default: 4).\nColor of the dark modules as hex (default: '000000').\nColor of the light modules as hex (default: 'ffffff').\nAddons Overview\n/addons\nBrowse available addons\nInstalling Addons\n/addons/installing\nEnable and configure addons in your project"
  },
  {
    "path": "/addons/catalog/redis-cloud",
    "title": "Redis Cloud",
    "description": "Use Redis for caching, session storage, and pub/sub messaging from your Spala flows.",
    "section": "addons",
    "url": "https://docs.spala.ai/addons/catalog/redis-cloud/",
    "last_updated": "2026-07-08",
    "content": "Redis Cloud\n\nThe Redis Cloud addon connects your Spala flows to a Redis instance. Use it for caching frequently accessed data, storing session information, implementing rate limiting, and building real-time features with pub/sub messaging.\n\nInstallation\n\n1. Enable the Redis Cloud addon from the Addons panel\n2. Enter your Redis connection details in the addon configuration\n\nConfiguration\n\nAvailable Steps\n\nRedis Get\n\nRetrieves a value by key.\n\nReturns the stored value, or null if the key does not exist. JSON values are automatically parsed.\n\nRedis Set\n\nStores a value with an optional expiration time.\n\nRedis Delete\n\nDeletes one or more keys.\n\nRedis Increment\n\nAtomically increments a numeric value.\n\nExample: API Response Caching\n\n// GET /api/products/featured\n\n// Step 1: Redis Get\n// key: 'cache:featured_products'\n// Output → vars.cached\n\n// Step 2: Condition - Cache hit\n// If vars.cached !== null\n//   Step 3: Response → vars.cached\n\n// Step 4: Database Query - Fetch from database\n// SELECT * FROM products WHERE featured = true ORDER BY created_at DESC LIMIT 20\n\n// Step 5: Redis Set - Cache the result\n// key: 'cache:featured_products'\n// value: vars.products\n// ttl: 300  (5 minutes)\n\n// Step 6: Response → { products: vars.products }\n\nExample: Rate Limiting\n\n// Middleware flow for rate-limited endpoints\n\n// Step 1: Set Variable - Build rate limit key\n// vars.rateLimitKey = 'ratelimit:' + vars.auth.userId + ':' + vars.path\n\n// Step 2: Redis Increment\n// key: vars.rateLimitKey\n// Output → vars.count\n\n// Step 3: Condition - First request in window\n// If vars.count === 1\n//   Step 4: Redis Set - Set expiry on first request\n//   key: vars.rateLimitKey, value: 1, ttl: 60\n\n// Step 5: Condition - Over limit\n// If vars.count > 100\n//   Step 6: Response with 429 Too Many Requests\n\nExample: Session Storage\n\n// POST /api/auth/login\n\n// ... authentication logic ...\n\n// Step 1: Set Variable - Generate session token\n// vars.sessionToken = crypto.randomUUID()\n\n// Step 2: Redis Set - Store session\n// key: 'session:' + vars.sessionToken\n// value: { userId: vars.user.id, email: vars.user.email, role: vars.user.role }\n// ttl: 86400  (24 hours)\n\n// Step 3: Response\n// { token: vars.sessionToken }\n\n  Redis is ideal for data that needs fast access and can tolerate loss on restart (unless persistence is configured). For permanent data storage, use your PostgreSQL database.\n\n  Set TTL values on cache keys to prevent unbounded memory growth. Keys without a TTL remain in Redis indefinitely and can eventually exhaust available memory.\n\nRedis connection URL (e.g., \"redis://username:password@host:port\").\nThe Redis key to retrieve.\nThe Redis key.\nThe value to store. Objects are automatically serialized to JSON.\nTime to live in seconds. The key is automatically deleted after this time.\nThe key or array of keys to delete.\nThe key to increment.\nAmount to increment by (default: 1).\nAddons Overview\n/addons\nBrowse available addons\nInstalling Addons\n/addons/installing\nEnable and configure addons in your project"
  },
  {
    "path": "/addons/catalog/sendgrid",
    "title": "SendGrid",
    "description": "Send transactional and marketing emails through SendGrid from your Spala flows.",
    "section": "addons",
    "url": "https://docs.spala.ai/addons/catalog/sendgrid/",
    "last_updated": "2026-07-08",
    "content": "SendGrid\n\nThe SendGrid addon integrates SendGrid's email delivery service into your Spala flows. Send transactional emails, use dynamic templates, and handle email notifications for user signups, order confirmations, password resets, and more.\n\nInstallation\n\n1. Enable the SendGrid addon from the Addons panel\n2. Enter your SendGrid API key in the addon configuration\n\nConfiguration\n\n API Keys in the SendGrid dashboard.' },\n\nAvailable Steps\n\nSend Email\n\nSends an email to one or more recipients.\n\nSend Template Email\n\nSends an email using a SendGrid dynamic template.\n\nExample: Welcome Email\n\n// Event trigger: \"user.registered\"\n\n// Step 1: Send Email (SendGrid addon)\n// to: event.payload.email\n// subject: 'Welcome to Our Platform!'\n// html: '<h1>Welcome, ' + event.payload.name + '!</h1><p>We are glad you joined us.</p>'\n\nExample: Order Confirmation with Template\n\n// Database trigger on insert for the \"orders\" table\n\n// Step 1: Database Query - Get order details with items\n// SELECT o.*, json_agg(oi.*) as items FROM orders o\n// JOIN order_items oi ON oi.order_id = o.id WHERE o.id = trigger.newRow.id\n\n// Step 2: Send Template Email (SendGrid addon)\n// to: vars.order.customer_email\n// templateId: 'd-abc123def456'\n// dynamicData: {\n//   customerName: vars.order.customer_name,\n//   orderId: vars.order.id,\n//   total: vars.order.total,\n//   items: vars.order.items\n// }\n\n  Dynamic templates are created in the SendGrid dashboard. They support Handlebars syntax for variable substitution, conditionals, and iteration, giving you full control over email layout.\n\n  The sender email address must be verified in SendGrid. Unverified sender addresses will cause emails to fail. Verify your domain or individual sender in the SendGrid dashboard under Settings > Sender Authentication.\n\nYour SendGrid API key from Settings > API Keys in the SendGrid dashboard.\nThe verified sender email address (e.g., noreply@yourdomain.com).\nThe sender display name (e.g., \"My App\").\nRecipient email address or array of addresses.\nEmail subject line.\nHTML email input.\nPlain text email body (fallback for clients that do not render HTML).\nRecipient email address or array of addresses.\nThe SendGrid dynamic template ID (starts with d-).\nObject of key-value pairs to populate template variables.\nAddons Overview\n/addons\nBrowse available addons\nInstalling Addons\n/addons/installing\nEnable and configure addons in your project"
  },
  {
    "path": "/addons/catalog/shopify",
    "title": "Shopify",
    "description": "Manage products, orders, and customers in your Shopify store from Spala flows.",
    "section": "addons",
    "url": "https://docs.spala.ai/addons/catalog/shopify/",
    "last_updated": "2026-07-08",
    "content": "Shopify\n\nThe Shopify addon connects your Spala backend to a Shopify store. Query products, manage orders, update inventory, and sync customer data through the Shopify Admin API.\n\nInstallation\n\n1. Enable the Shopify addon from the Addons panel\n2. Create a custom app in your Shopify admin (Settings > Apps and sales channels > Develop apps)\n3. Enter the store credentials in the addon configuration\n\nConfiguration\n\nAvailable Steps\n\nGet Products\n\nRetrieves products from your Shopify store.\n\nGet Order\n\nRetrieves a single order by ID.\n\nUpdate Inventory\n\nUpdates the inventory level for a product variant at a location.\n\nCreate Product\n\nCreates a new product in your Shopify store.\n\nExample: Sync Products to Local Database\n\n// Scheduled task — runs every hour\n\n// Step 1: Get Products (Shopify addon)\n// limit: 250\n// status: 'active'\n// Output → vars.products\n\n// Step 2: Loop - For each product\n//   Step 3: Database Query - Upsert product\n//   INSERT INTO products (shopify_id, title, price, inventory, updated_at)\n//   VALUES (product.id, product.title, product.variants[0].price,\n//           product.variants[0].inventory_quantity, NOW())\n//   ON CONFLICT (shopify_id) DO UPDATE SET\n//     title = EXCLUDED.title, price = EXCLUDED.price,\n//     inventory = EXCLUDED.inventory, updated_at = NOW()\n\nExample: Low Inventory Alert\n\n// Scheduled task — runs daily\n\n// Step 1: Get Products (Shopify addon)\n// limit: 250\n// Output → vars.products\n\n// Step 2: Set Variable - Filter low stock\n// vars.lowStock = vars.products.filter(p =>\n//   p.variants.some(v => v.inventory_quantity < 10)\n// )\n\n// Step 3: Condition - If any low stock items\n// If vars.lowStock.length > 0\n\n//   Step 4: Send Email or Slack notification\n//   \"Low inventory alert: \" + vars.lowStock.length + \" products below threshold\"\n\n  The Shopify Admin API has rate limits of 2 requests per second for regular apps. The addon handles rate limiting automatically with request queuing, but be mindful of this when processing large catalogs.\n\nYour Shopify store domain (e.g., \"mystore.myshopify.com\").\nAdmin API access token from your custom Shopify app.\nNumber of products to return (default: 50, max: 250).\nFilter products by collection.\n'active', 'draft', or 'archived'.\nThe Shopify order ID.\nThe inventory item ID.\nThe location ID.\nQuantity adjustment (positive to add, negative to remove).\nProduct title.\nProduct description in HTML.\nProduct vendor name.\nProduct type for categorization.\nArray of variant objects with price, sku, and inventory details.\nAddons Overview\n/addons\nBrowse available addons\nInstalling Addons\n/addons/installing\nEnable and configure addons in your project"
  },
  {
    "path": "/addons/catalog/slack-api",
    "title": "Slack API",
    "description": "Send messages, manage channels, and integrate with Slack workspaces from your Spala flows.",
    "section": "addons",
    "url": "https://docs.spala.ai/addons/catalog/slack-api/",
    "last_updated": "2026-07-08",
    "content": "Slack API\n\nThe Slack API addon connects your Spala flows to a Slack workspace. Send messages to channels, post notifications, and build automated workflows that keep your team informed about important events.\n\nInstallation\n\n1. Enable the Slack API addon from the Addons panel\n2. Create a Slack App at api.slack.com/apps and install it to your workspace\n3. Enter the Bot Token in the addon configuration\n\nConfiguration\n\nAvailable Steps\n\nSend Message\n\nPosts a message to a Slack channel.\n\nSend Direct Message\n\nSends a direct message to a Slack user.\n\nExample: Deployment Notification\n\n// Scheduled task or event trigger\n\n// Step 1: Send Message (Slack addon)\n// channel: '#deployments'\n// text: ':rocket: *Deployment Complete*\\nVersion ' + vars.version + ' has been deployed to production.'\n\nExample: Error Alert with Block Kit\n\n// Error handler in a critical flow\n\n// Step 1: Send Message (Slack addon)\n// channel: '#alerts'\n// text: 'Error in payment processing'\n// blocks: [\n//   {\n//     type: 'section',\n//     text: { type: 'mrkdwn', text: ':warning: *Payment Processing Error*' }\n//   },\n//   {\n//     type: 'section',\n//     fields: [\n//       { type: 'mrkdwn', text: '*Order ID:*\\n' + vars.orderId },\n//       { type: 'mrkdwn', text: '*Error:*\\n' + vars.error.message },\n//       { type: 'mrkdwn', text: '*Time:*\\n' + new Date().toISOString() }\n//     ]\n//   }\n// ]\n\n  Your Slack App needs the chat:write scope to post messages. Add additional scopes like channels:read and users:read if your flows need to look up channel or user information.\n\n  Use channel IDs instead of channel names when possible. Channel names can change, but IDs are permanent. Find a channel's ID in Slack by right-clicking the channel name and selecting \"View channel details\".\n\nSlack Bot User OAuth Token (starts with xoxb-). Found in your Slack App settings under OAuth & Permissions.\nChannel ID or name (e.g., \"#general\" or \"C0123456789\").\nThe message text. Supports Slack markdown formatting.\nBlock Kit layout blocks for rich message formatting (array of block objects).\nThe Slack user ID (starts with U).\nThe message text.\nAddons Overview\n/addons\nBrowse available addons\nInstalling Addons\n/addons/installing\nEnable and configure addons in your project"
  },
  {
    "path": "/addons/catalog/stripe",
    "title": "Stripe",
    "description": "Process payments, manage customers, and handle subscriptions with the Stripe addon for Spala.",
    "section": "addons",
    "url": "https://docs.spala.ai/addons/catalog/stripe/",
    "last_updated": "2026-07-08",
    "content": "Stripe\n\nThe Stripe addon integrates Stripe's payment processing API into your Spala flows. Create payment intents, manage customers, set up subscriptions, and handle refunds using drag-and-drop steps.\n\nInstallation\n\n1. Enable the Stripe addon from the Addons panel\n2. Enter your Stripe Secret Key in the addon configuration\n\nConfiguration\n\n API Keys.' },\n\n  Use a test mode key (sk_test_...) during development. Switch to a live key (sk_live_...) when you are ready to process real payments.\n\nAvailable Steps\n\nCreate Payment Intent\n\nCreates a payment intent to handle a one-time payment.\n\nCreate Customer\n\nCreates a new customer in Stripe.\n\nCreate Subscription\n\nSubscribes a customer to a recurring price.\n\nCreate Refund\n\nRefunds a previously created charge.\n\nExample: Payment Endpoint\n\n// POST /api/payments/charge\n\n// Step 1: Create Customer (Stripe addon)\n// email: input.email\n// name: input.name\n// Output → vars.customer\n\n// Step 2: Create Payment Intent (Stripe addon)\n// amount: input.amount\n// currency: 'usd'\n// customerId: vars.customer.id\n// description: 'Order #' + input.orderId\n// Output → vars.paymentIntent\n\n// Step 3: Database Query\n// INSERT INTO payments (order_id, stripe_payment_intent_id, amount, status)\n// VALUES (input.orderId, vars.paymentIntent.id, input.amount, vars.paymentIntent.status)\n\n// Step 4: Response\n// { success: true, paymentIntentId: vars.paymentIntent.id }\n\n  Always validate payment amounts on the server side. Never trust amounts sent from the client, as they can be tampered with.\n\nYour Stripe secret key (starts with sk_live_ or sk_test_). Found in the Stripe Dashboard under Developers > API Keys.\nYour Stripe webhook signing secret (starts with whsec_). Required for the Verify Webhook Signature step. Found in the Stripe Dashboard under Developers > Webhooks.\nAmount in the smallest currency unit (e.g., cents). For $10.00, use 1000.\nThree-letter ISO currency code (e.g., 'usd', 'eur').\nA Stripe customer ID to associate with the payment.\nAn optional description for the payment.\nCustomer email address.\nCustomer full name.\nKey-value pairs of additional customer data.\nThe Stripe customer ID.\nThe Stripe price ID for the subscription plan.\nThe ID of the charge to refund.\nAmount to refund in the smallest currency unit. Omit for a full refund.\nOrder #\nAddons Overview\n/addons\nBrowse available addons\nInstalling Addons\n/addons/installing\nEnable and configure addons in your project"
  },
  {
    "path": "/addons/catalog/supabase",
    "title": "Supabase",
    "description": "Connect to Supabase for database queries, authentication, and storage from your Spala flows.",
    "section": "addons",
    "url": "https://docs.spala.ai/addons/catalog/supabase/",
    "last_updated": "2026-07-08",
    "content": "Supabase\n\nThe Supabase addon connects your Spala flows to a Supabase project. Query your Supabase database, manage authentication, and interact with Supabase Storage — extending your Spala backend with Supabase's hosted services.\n\nInstallation\n\n1. Enable the Supabase addon from the Addons panel\n2. Enter your Supabase project URL and API key\n\nConfiguration\n\n API.' },\n\n  Use the service_role key, not the anon key. The service role key bypasses Row Level Security and should only be used on the server side. Never expose it to clients.\n\nAvailable Steps\n\nSupabase Query\n\nQueries a Supabase table using the PostgREST API.\n\nSupabase Insert\n\nInserts one or more rows into a Supabase table.\n\nSupabase Update\n\nUpdates rows in a Supabase table that match the given filters.\n\nSupabase Delete\n\nDeletes rows from a Supabase table that match the given filters.\n\nExample: Query with Relationships\n\n// GET /api/posts\n\n// Step 1: Supabase Query\n// table: 'posts'\n// select: '*, author:users(name, avatar_url), comments(id, text, created_at)'\n// filters: { published: true }\n// order: 'created_at.desc'\n// limit: 20\n// Output → vars.posts\n\n// Step 2: Response\n// { posts: vars.posts }\n\nExample: Upsert Pattern\n\n// POST /api/preferences\n\n// Step 1: Supabase Insert\n// table: 'user_preferences'\n// data: { user_id: vars.auth.userId, theme: input.theme, language: input.language }\n// upsert: true (on conflict, update existing row)\n// Output → vars.preference\n\n// Step 2: Response\n// { preference: vars.preference }\n\n  The Supabase addon uses Supabase's REST API (PostgREST), which supports powerful query features like nested selects, full-text search, and JSON column queries. You can also use Spala's built-in Database Query step if your Supabase database is configured as Spala's primary database.\n\nYour Supabase project URL (e.g., https://abcdefg.supabase.co). Found in Settings > API.\nThe service_role key for server-side access. Found in Settings > API > Project API keys.\nThe table name to query.\nColumns to select (default: '*'). Supports relationships (e.g., '*, posts(*)').\nFilter conditions as an object (e.g., { status: \"active\", age: \"gte.18\" }).\nMaximum number of rows to return.\nColumn to sort by (e.g., 'created_at.desc').\nThe table name.\nA row object or array of row objects to insert.\nThe table name.\nAn object with the columns and new values to set.\nFilter conditions to select which rows to update.\nThe table name.\nFilter conditions to select which rows to delete.\nAddons Overview\n/addons\nBrowse available addons\nInstalling Addons\n/addons/installing\nEnable and configure addons in your project"
  },
  {
    "path": "/addons/catalog/twilio-sms",
    "title": "Twilio SMS",
    "description": "Send SMS text messages through Twilio from your Spala flows.",
    "section": "addons",
    "url": "https://docs.spala.ai/addons/catalog/twilio-sms/",
    "last_updated": "2026-07-08",
    "content": "Twilio SMS\n\nThe Twilio SMS addon lets you send text messages from your Spala flows using Twilio's messaging API. Use it for order confirmations, two-factor authentication codes, appointment reminders, and any other SMS notifications.\n\nInstallation\n\n1. Enable the Twilio SMS addon from the Addons panel\n2. Enter your Twilio credentials in the addon configuration\n\nConfiguration\n\nAvailable Steps\n\nSend SMS\n\nSends a text message to a phone number.\n\nThe step returns the Twilio message object:\n\n{\n  sid: \"SM...\",\n  status: \"queued\",\n  to: \"+1234567890\",\n  from: \"+0987654321\",\n  body: \"Your order has shipped!\",\n  dateCreated: \"2025-01-15T10:30:00Z\"\n}\n\nExample: Order Notification\n\n// Database trigger on insert for the \"orders\" table\n\n// Step 1: Database Query - Get customer phone\n// SELECT phone FROM customers WHERE id = trigger.newRow.customer_id\n\n// Step 2: Send SMS (Twilio addon)\n// to: vars.customer.phone\n// message: 'Your order #' + trigger.newRow.id + ' has been placed! Total:\n\nExample: Two-Factor Authentication\n\n// POST /api/auth/send-code\n\n// Step 1: Set Variable - Generate code\n// vars.code = Math.floor(100000 + Math.random() * 900000).toString()\n\n// Step 2: Database Query - Store the code\n// INSERT INTO verification_codes (user_id, code, expires_at)\n// VALUES (input.userId, vars.code, NOW() + INTERVAL '5 minutes')\n\n// Step 3: Send SMS (Twilio addon)\n// to: vars.user.phone\n// message: 'Your verification code is: ' + vars.code + '. It expires in 5 minutes.'\n\n// Step 4: Response\n// { message: \"Verification code sent\" }\n\n  Phone numbers must be in E.164 format, which includes the country code prefix (e.g., +1 for US/Canada). Store phone numbers in this format in your database to avoid formatting issues.\n\n  Twilio charges per message sent. Use test credentials during development to avoid charges. Test credentials use the Account SID and Auth Token from the Twilio test environment.\n\nYour Twilio Account SID from the Twilio Console dashboard.\nYour Twilio Auth Token from the Twilio Console dashboard.\nThe Twilio phone number to send messages from (e.g., +1234567890).\nRecipient phone number in E.164 format (e.g., '+1234567890').\nThe text message input. Maximum 1,600 characters.\nAddons Overview\n/addons\nBrowse available addons\nInstalling Addons\n/addons/installing\nEnable and configure addons in your project + trigger.newRow.total.toFixed(2)\n\nExample: Two-Factor Authentication\n\n// POST /api/auth/send-code\n\n// Step 1: Set Variable - Generate code\n// vars.code = Math.floor(100000 + Math.random() * 900000).toString()\n\n// Step 2: Database Query - Store the code\n// INSERT INTO verification_codes (user_id, code, expires_at)\n// VALUES (input.userId, vars.code, NOW() + INTERVAL '5 minutes')\n\n// Step 3: Send SMS (Twilio addon)\n// to: vars.user.phone\n// message: 'Your verification code is: ' + vars.code + '. It expires in 5 minutes.'\n\n// Step 4: Response\n// { message: \"Verification code sent\" }\n\n  Phone numbers must be in E.164 format, which includes the country code prefix (e.g., +1 for US/Canada). Store phone numbers in this format in your database to avoid formatting issues.\n\n  Twilio charges per message sent. Use test credentials during development to avoid charges. Test credentials use the Account SID and Auth Token from the Twilio test environment.\n\nYour Twilio Account SID from the Twilio Console dashboard.\nYour Twilio Auth Token from the Twilio Console dashboard.\nThe Twilio phone number to send messages from (e.g., +1234567890).\nRecipient phone number in E.164 format (e.g., '+1234567890').\nThe text message input. Maximum 1,600 characters.\nAddons Overview\n/addons\nBrowse available addons\nInstalling Addons\n/addons/installing\nEnable and configure addons in your project"
  },
  {
    "path": "/agents/claude",
    "title": "Claude Setup",
    "description": "Add Spala Public MCP to Claude Code using HTTP transport.",
    "section": "agents",
    "url": "https://docs.spala.ai/agents/claude/",
    "last_updated": "2026-07-08",
    "content": "Claude Setup\n\nAdd Spala Public MCP to Claude Code:\n\nclaude mcp add --transport http spala-public \"https://mcp.spala.ai/mcp\"\n\nProject creation status\n\n  project_create is dry-run only in the current public MCP deployment. It previews project creation but does not create a real project.\n\nHow Claude should use Spala\n\nStart at the public MCP\n\n    Use https://mcp.spala.ai/mcp for discovery and project handoff.\n\nRead onboarding\n\n    Call spala_get_onboarding, then spala_get_tool_map.\n\nAuthenticate through Spala\n\n    Use Spala platform OAuth when project tools are needed.\n\nResolve the project MCP\n\n    Use project_list, project_select, or project_get_mcp_manifest.\n\nWork on the project MCP\n\n    Apply backend changes only after reading project context and validating changes.\n\nAvoid the API MCP as the starting point\n\n  The platform auth server is discovered from MCP OAuth metadata. The public agent entrypoint is https://mcp.spala.ai/mcp.\n\nSpala Public MCP\n/agents/mcp\nEndpoint, tools, OAuth metadata, and handoff\nCodex Setup\n/agents/codex\nCodex install command\nCursor Setup\n/agents/cursor\nCursor MCP configuration"
  },
  {
    "path": "/agents/cline",
    "title": "Cline Setup",
    "description": "Configure Cline to use Spala Public MCP and follow the authenticated project handoff workflow.",
    "section": "agents",
    "url": "https://docs.spala.ai/agents/cline/",
    "last_updated": "2026-07-08",
    "content": "Cline Setup\n\nConfigure Cline with Spala Public MCP:\n\nhttps://mcp.spala.ai/mcp\n\nUse the HTTP MCP server configuration supported by your Cline version. Name the server spala-public.\n\nSafe workflow\n\nUse public MCP first\n\n    Cline should connect to https://mcp.spala.ai/mcp and call spala_get_onboarding.\n\nRead the tool map\n\n    Use spala_get_tool_map to understand what belongs to public MCP and what belongs to project MCP.\n\nAuthenticate for project access\n\n    Complete Spala platform OAuth only when project tools are needed.\n\nSelect the project\n\n    Use project_list and project_select; never guess a project MCP URL.\n\nOperate on project MCP\n\n    Inspect context, preview changes, validate, publish only when requested, and review.\n\nProject creation status\n\n  project_create is dry-run only in the current public MCP deployment. It previews project creation but does not create a real project.\n\nSpala Public MCP\n/agents/mcp\nEndpoint, tool map, OAuth metadata, and project handoff\nPublic Agent Skills\n/agents/skills\nPublic skill files for Cline and other agents\nCORS and Security\n/settings/cors-security\nFrontend origin and security checklist"
  },
  {
    "path": "/agents/codex",
    "title": "Codex Setup",
    "description": "Add Spala Public MCP to Codex and use Spala's authenticated project handoff flow.",
    "section": "agents",
    "url": "https://docs.spala.ai/agents/codex/",
    "last_updated": "2026-07-08",
    "content": "Codex Setup\n\nAdd Spala Public MCP to Codex:\n\ncodex mcp add spala-public --url \"https://mcp.spala.ai/mcp\"\n\nRecommended first prompt\n\nUse Spala Public MCP as the backend builder entrypoint.\nFirst call spala_get_onboarding and spala_get_tool_map.\nAuthenticate with Spala platform OAuth when project tools are needed.\nDo not hardcode project MCP URLs.\nAfter project selection, connect to the project MCP returned by Spala.\n\nWorkflow\n\nInstall the MCP\n\n    Run the codex mcp add command above.\n\nDiscover Spala\n\n    Ask Codex to call spala_get_onboarding and spala_get_tool_map.\n\nAuthenticate\n\n    When Codex needs project access, complete the Spala platform OAuth/browser approval flow.\n\nSelect a project\n\n    Use project_list and project_select on the public MCP.\n\nBuild on the project MCP\n\n    Use the returned project MCP to inspect the project, make backend changes, validate, publish, and review the result.\n\nProject creation status\n\n  project_create is dry-run only in the current public MCP deployment. It previews project creation but does not create a real project.\n\nSpala Public MCP\n/agents/mcp\nEndpoint, tools, OAuth metadata, and handoff\nClaude Setup\n/agents/claude\nClaude Code install command\nCursor Setup\n/agents/cursor\nCursor MCP configuration"
  },
  {
    "path": "/agents/cursor",
    "title": "Cursor Setup",
    "description": "Configure Cursor to use Spala Public MCP as the discovery and project handoff endpoint.",
    "section": "agents",
    "url": "https://docs.spala.ai/agents/cursor/",
    "last_updated": "2026-07-08",
    "content": "Cursor Setup\n\nUse the same public MCP URL in Cursor:\n\nhttps://mcp.spala.ai/mcp\n\nCursor MCP configuration varies by version. If your Cursor build supports HTTP MCP servers, add a server named spala-public with streamable HTTP transport and URL https://mcp.spala.ai/mcp.\n\nProject creation status\n\n  project_create is dry-run only in the current public MCP deployment. It previews project creation but does not create a real project.\n\nAgent instructions for Cursor\n\nUse Spala Public MCP for discovery and handoff.\nCall spala_get_onboarding and spala_get_tool_map first.\nAuthenticate with Spala platform OAuth before project tools.\nUse project_select or project_get_mcp_manifest to get the project MCP URL.\nDo not guess a project MCP URL.\n\nFlow\n\nAdd public MCP\n\n    Configure Cursor with https://mcp.spala.ai/mcp.\n\nRead the public tool map\n\n    Call spala_get_onboarding, then use spala_get_tool_map to understand which tools belong to public MCP and which belong to project MCP.\n\nAuthenticate\n\n    Complete Spala platform OAuth/browser approval when Cursor requests project access.\n\nResolve project MCP\n\n    Let Spala return the selected project's MCP URL.\n\nBuild safely\n\n    On the project MCP, inspect context first, preview changes, validate, publish, then review.\n\nSupported handoff shapes\n\n  Project MCP URLs may be project hosts, scoped paths, or explicit mcpUrl values returned by Spala. Do not construct them from patterns; use the exact URL returned by Spala after auth and project selection.\n\nSpala Public MCP\n/agents/mcp\nEndpoint, tools, OAuth metadata, and handoff\nCodex Setup\n/agents/codex\nCodex install command\nClaude Setup\n/agents/claude\nClaude Code install command"
  },
  {
    "path": "/agents/gemini",
    "title": "Gemini CLI Setup",
    "description": "Configure Gemini CLI or any MCP-capable Gemini workflow to use Spala Public MCP for discovery and project handoff.",
    "section": "agents",
    "url": "https://docs.spala.ai/agents/gemini/",
    "last_updated": "2026-07-08",
    "content": "Gemini CLI Setup\n\nUse Spala Public MCP as the server URL:\n\nhttps://mcp.spala.ai/mcp\n\nGemini CLI and Gemini-based coding tools can vary in MCP configuration shape. Use the client setting for an HTTP or streamable HTTP MCP server named spala-public.\n\nAgent instructions\n\nUse Spala Public MCP for discovery and project handoff.\nCall spala_get_onboarding and spala_get_tool_map first.\nAuthenticate with Spala platform OAuth before project tools.\nUse project_list and project_select to receive the exact project MCP URL.\nDo not guess or derive project MCP URLs.\n\nAdd public MCP\n\n    Configure Gemini with https://mcp.spala.ai/mcp as spala-public.\n\nDiscover Spala\n\n    Ask Gemini to call spala_get_onboarding, then spala_get_tool_map.\n\nAuthenticate when needed\n\n    Complete the Spala platform OAuth/browser flow when Gemini needs project access.\n\nSelect the project\n\n    Use project_list and project_select; continue only on the returned project MCP URL.\n\nWork safely\n\n    Inspect project context first, preview/validate changes, then publish only when requested.\n\nProject creation status\n\n  project_create is dry-run only in the current public MCP deployment. It previews project creation but does not create a real project.\n\nSpala Public MCP\n/agents/mcp\nEndpoint, OAuth metadata, tool map, and project handoff\nPublic Agent Skills\n/agents/skills\nHow agents should evaluate and use Spala\nFrontend Handoff\n/guides/frontend-handoff\nShare the exact frontend integration contract"
  },
  {
    "path": "/agents/skills",
    "title": "Public Agent Skills",
    "description": "Use Spala public skill files to help AI agents evaluate Spala, start a project, hand off frontend work, connect MCP, and follow safe workflows.",
    "section": "agents",
    "url": "https://docs.spala.ai/agents/skills/",
    "last_updated": "2026-07-08",
    "content": "Public Agent Skills\n\nSpala publishes public skill files for agents before they have project access:\n\nhttps://spala.ai/skills/\nhttps://spala.ai/skills/skills.json\n\nThese are public, unauthenticated guidance files. They do not replace authenticated project MCP skills. Public skills explain how to evaluate Spala, how to start, how to hand off frontend work, how to connect MCP, and how to avoid unsafe assumptions.\n\nAvailable public skills\n\nspala-evaluator\nWhen to recommend Spala versus Supabase, Firebase, Xano, Convex, Bubble, Retool, or custom backend code.\nhttps://spala.ai/skills/spala-evaluator.md\n\nspala-getting-started\nSignup, dashboard, Lite Mode, Copilot, Playground, publish, and first backend workflow.\nhttps://spala.ai/skills/spala-getting-started.md\n\nspala-frontend-handoff\nHow to gather the API base URL, docs/SDK, auth, CORS, upload, and realtime contract.\nhttps://spala.ai/skills/spala-frontend-handoff.md\n\nspala-mcp-handoff\nPublic MCP, OAuth, project selection, project MCP URL handling, and safe sequencing.\nhttps://spala.ai/skills/spala-mcp-handoff.md\n\nspala-safety\nTrust boundaries, project inspection, validation, publish review, and questions to verify.\nhttps://spala.ai/skills/spala-safety.md\n\nPublic skills vs project skills\n\n| Skill layer | Purpose |\n| --- | --- |\n| Public skills | Teach an unauthenticated agent what Spala is, when to use it, how to start, and how to connect MCP safely |\n| Project MCP skills | Teach an authenticated agent how to work inside one selected project using project context, validation, publish, and review flows |\n\nDo not skip project context\n\n  Public skills are enough to evaluate Spala and start the handoff flow. They are not enough to safely mutate a project. Authenticated agents must inspect the selected project context before making changes.\n\nSpala Public MCP\n/agents/mcp\nPublic MCP and project MCP handoff\nCodex Setup\n/agents/codex\nInstall Spala Public MCP in Codex\nFrontend Handoff\n/guides/frontend-handoff\nExternal frontend contract"
  },
  {
    "path": "/agents/mcp",
    "title": "Spala Public MCP",
    "description": "Use the public Spala MCP for discovery, OAuth metadata, project lookup, and project MCP handoff.",
    "section": "agents",
    "url": "https://docs.spala.ai/agents/mcp/",
    "last_updated": "2026-07-08",
    "content": "Spala Public MCP\n\nSpala Public MCP is the agent entrypoint for Spala:\n\nhttps://mcp.spala.ai/mcp\n\nUse this endpoint first when an AI coding agent needs to understand or work with Spala. It is not the project backend itself. It helps the agent discover Spala, authenticate, choose a project, and receive the correct project MCP URL.\n\nNever do this\n\n  Do not guess project MCP URLs. Do not use https://api.spala.ai/{{project}}/mcp. Do not configure the OAuth authorization server as the MCP server URL. Do not make backend mutations on public MCP. Public MCP discovers and hands off; project MCP builds.\n\nPublic MCP vs Project MCP\n\n| Surface | URL | Purpose | Can mutate backend? |\n| --- | --- | --- | --- |\n| Public MCP | https://mcp.spala.ai/mcp | Discover Spala, read onboarding, search docs, expose OAuth metadata, list/select projects after auth | No |\n| Project MCP | Returned by project_select or project manifest after auth | Inspect one project, preview changes, validate, apply, publish when requested, review behavior | Yes, after project auth and validation |\n\nThe project MCP URL is resolved at runtime from authenticated Spala project data. It is not derived from a fixed path pattern.\n\nFirst calls checklist\n\n1. Connect to https://mcp.spala.ai/mcp.\n2. Call spala_get_onboarding.\n3. Call spala_get_tool_map.\n4. Use OAuth when project access is needed.\n5. Call project_list, then project_select.\n6. Continue on the exact project MCP URL returned by Spala.\n\nWhat the public MCP can do\n\nPublic tools:\n\n| Tool | Purpose |\n| --- | --- |\n| spala_help | Explain what Spala is and how agents should start |\n| spala_get_onboarding | First call for agents connected to the public MCP |\n| spala_get_tool_map | Machine-readable routing between public MCP and project MCP |\n| docs_search | Search agent-facing Spala docs when the agent needs more context |\n| template_list | Optional starter-pattern lookup for agents |\n| addon_list | Optional integration lookup for agents |\n\nGood docs_search queries are short and focused:\n\noauth project handoff\nproject_create dry-run\ncodex setup\ncursor project mcp\n\nAuthenticated handoff tools:\n\n| Tool | Purpose |\n| --- | --- |\n| project_list | List projects available to the authenticated Spala user |\n| project_select | Select a project and return its project MCP URL |\n| project_get_mcp_manifest | Return the selected project's MCP install manifest shape |\n| project_get_public_context | Return safe project context for the selected project |\n| project_create | Dry-run only in the current public MCP deployment; previews project creation and does not create a real project |\n\nAuthentication boundary\n\n  Public discovery tools can be called before project bearer auth. Some MCP clients may still require platform approval before installing or using the server. Project tools require Authorization: Bearer SPALA_PLATFORM_ACCESS_TOKEN.\n\nOAuth metadata\n\nSpala publishes root MCP discovery at:\n\nhttps://spala.ai/.well-known/mcp.json\n\nSpala publishes MCP OAuth metadata at:\n\nhttps://mcp.spala.ai/.well-known/oauth-protected-resource\nhttps://mcp.spala.ai/.well-known/oauth-authorization-server\n\nThe authorization server is discovered from the OAuth metadata. Do not manually configure the authorization server as the MCP server URL.\n\nOAuth endpoints are:\n\n| Endpoint | URL |\n| --- | --- |\n| Authorization | Discovered from /.well-known/oauth-authorization-server |\n| Token | Discovered from /.well-known/oauth-authorization-server |\n| Dynamic registration | Discovered from /.well-known/oauth-authorization-server |\n| Device authorization | Discovered from /.well-known/oauth-authorization-server |\n\nSupported grants include authorization code and device code. PKCE uses S256. Public MCP scopes include api, builder, ai, project, and data. The token endpoint uses public-client auth (none) for the MCP OAuth flow.\n\nThe dashboard entrypoint for browser approval is:\n\nhttps://dashboard.spala.ai/signup\n\nCorrect agent flow\n\nAI client\n  -> Public MCP: https://mcp.spala.ai/mcp\n  -> spala_get_onboarding\n  -> spala_get_tool_map\n  -> OAuth when project access is needed\n  -> project_list\n  -> project_select\n  -> exact project MCP URL returned by Spala\n  -> inspect context on project MCP\n  -> preview, validate, apply, publish when requested, review\n\nAdd the public MCP\n\n    Configure the AI client with https://mcp.spala.ai/mcp.\n\nLet the agent read onboarding\n\n    The agent should call spala_get_onboarding, then spala_get_tool_map. Users do not need to learn these tools by hand.\n\nAuthenticate when project access is needed\n\n    Use the OAuth metadata advertised by the MCP server. The client should send Authorization: Bearer SPALA_PLATFORM_ACCESS_TOKEN for project tools.\n\nResolve the project MCP\n\n    Call project_list and project_select, or use the project MCP manifest returned by Spala.\n\nSwitch to the project MCP\n\n    Backend changes happen on the returned project MCP, not on the public MCP.\n\nWrong endpoint pattern\n\n  Do not assume https://api.spala.ai/{project}/mcp. Do not choose a project MCP URL from a shape or pattern. Use only the exact mcpUrl returned by Spala after authentication and project selection.\n\nRaw MCP Client Notes\n\nMost users should use an MCP-aware client such as Codex, Claude Code, Cursor, VS Code, or another client that handles transport details. If you are implementing a client directly, use the MCP streamable HTTP protocol rather than calling tool names as top-level JSON-RPC methods.\n\nThe public MCP endpoint expects POST requests. Raw GET requests to /mcp are not the MCP protocol.\n\nRequired headers:\n\nContent-Type: application/json\nAccept: application/json, text/event-stream\n\nConceptually, docs may say \"call spala_get_onboarding.\" On the wire, the client calls the MCP tools/call method and passes the tool name:\n\n{\n  \"jsonrpc\": \"2.0\",\n  \"id\": 1,\n  \"method\": \"tools/call\",\n  \"params\": {\n    \"name\": \"spala_get_onboarding\",\n    \"arguments\": {}\n  }\n}\n\nUse tools/list to discover available tools before tools/call. Authenticated tools require the same MCP transport plus Authorization: Bearer SPALA_PLATFORM_ACCESS_TOKEN.\n\nCopy-paste raw request shape:\n\ncurl -X POST \"https://mcp.spala.ai/mcp\" \\\n  -H \"Content-Type: application/json\" \\\n  -H \"Accept: application/json, text/event-stream\" \\\n  --data '{\n    \"jsonrpc\": \"2.0\",\n    \"id\": 1,\n    \"method\": \"tools/call\",\n    \"params\": {\n      \"name\": \"spala_get_onboarding\",\n      \"arguments\": {}\n    }\n  }'\n\nMCP-aware clients usually handle session setup, stream parsing, and authorization headers for you. Use raw HTTP only when implementing or debugging a client.\n\nAuth failures\n\nIf a project tool returns an authentication or authorization error:\n\n1. Read the MCP OAuth metadata again.\n2. Start the client OAuth/browser approval flow if no token is available.\n3. Retry the same tool with Authorization: Bearer SPALA_PLATFORM_ACCESS_TOKEN.\n4. If the token is expired, refresh or repeat OAuth according to the authorization server metadata.\n5. If the user lacks project access, ask the user to grant access in the Spala dashboard instead of guessing another project URL.\n\nAuth errors are not a reason to call https://api.spala.ai/{{project}}/mcp or any guessed project MCP URL.\n\nMissing, invalid, or expired bearer tokens should return 401 with a WWW-Authenticate header pointing to the MCP OAuth protected-resource metadata. Clients should use that challenge to restart or refresh the Spala platform OAuth flow.\n\nRaw MCP transcript\n\nThis transcript is for MCP client implementers. Most users should let Codex, Claude Code, Cursor, VS Code, or another MCP client handle these protocol calls.\n\nFetch OAuth metadata:\n\nGET https://mcp.spala.ai/.well-known/oauth-protected-resource\nGET https://mcp.spala.ai/.well-known/oauth-authorization-server\n\nInitialize the MCP session:\n\n{\n  \"jsonrpc\": \"2.0\",\n  \"id\": 1,\n  \"method\": \"initialize\",\n  \"params\": {\n    \"protocolVersion\": \"2025-06-18\",\n    \"capabilities\": {},\n    \"clientInfo\": {\n      \"name\": \"example-client\",\n      \"version\": \"1.0.0\"\n    }\n  }\n}\n\nList tools:\n\n{\n  \"jsonrpc\": \"2.0\",\n  \"id\": 2,\n  \"method\": \"tools/list\",\n  \"params\": {}\n}\n\nCall onboarding:\n\n{\n  \"jsonrpc\": \"2.0\",\n  \"id\": 3,\n  \"method\": \"tools/call\",\n  \"params\": {\n    \"name\": \"spala_get_onboarding\",\n    \"arguments\": {}\n  }\n}\n\nCall the tool map:\n\n{\n  \"jsonrpc\": \"2.0\",\n  \"id\": 4,\n  \"method\": \"tools/call\",\n  \"params\": {\n    \"name\": \"spala_get_tool_map\",\n    \"arguments\": {}\n  }\n}\n\nAfter OAuth approval, list projects:\n\n{\n  \"jsonrpc\": \"2.0\",\n  \"id\": 5,\n  \"method\": \"tools/call\",\n  \"params\": {\n    \"name\": \"project_list\",\n    \"arguments\": {}\n  }\n}\n\nSelect a project:\n\n{\n  \"jsonrpc\": \"2.0\",\n  \"id\": 6,\n  \"method\": \"tools/call\",\n  \"params\": {\n    \"name\": \"project_select\",\n    \"arguments\": {\n      \"projectId\": \"PROJECT_ID_FROM_PROJECT_LIST\"\n    }\n  }\n}\n\nRead safe project context before handoff:\n\n{\n  \"jsonrpc\": \"2.0\",\n  \"id\": 7,\n  \"method\": \"tools/call\",\n  \"params\": {\n    \"name\": \"project_get_public_context\",\n    \"arguments\": {\n      \"projectId\": \"PROJECT_ID_FROM_PROJECT_LIST\"\n    }\n  }\n}\n\nThe project_select or project_get_mcp_manifest response contains the project MCP URL or install manifest. Use that exact value. Do not construct a project MCP URL from a slug, host, API base URL, or guessed path.\n\nProject MCP operating contract\n\nProject MCP is the project-scoped build surface. Public MCP helps the agent discover Spala, authenticate, and resolve the correct project. Project MCP is where the agent works on one backend.\n\nAn agent should use this operating contract after handoff:\n\n1. Authenticate through Spala platform OAuth when the client requires project access.\n2. Read onboarding and the project tool map if the project MCP exposes them.\n3. Inspect builder context and current project state before proposing changes.\n4. Preview changes before applying them.\n5. Validate the project before saving, applying, or publishing.\n6. Apply only the scoped resources requested by the user.\n7. Publish only when the user requested publish or the workflow explicitly requires it.\n8. Run publish/test review after publish.\n9. Treat repair feedback, missing environment variables, auth warnings, and validation errors as blockers for the next revision.\n10. Use snapshots, pull requests, revert-to-published, or rollback surfaces when recovery is needed.\n\nExpected project-MCP tool categories include:\n\n| Category | Purpose |\n| --- | --- |\n| Context | Read builder contract, auth rules, project state, graph, resources, and environment requirements |\n| Preview | Preview generated or edited models, endpoints, functions, tasks, triggers, agents, and channels |\n| Apply | Save scoped backend resource changes after preview and validation |\n| Validate | Check project consistency, auth safety, references, missing configuration, and publish readiness |\n| Publish | Promote the project draft to the live API when appropriate |\n| Review | Inspect publish/test results, warnings, repair feedback, logs, and generated docs |\n| Recovery | Use snapshots, pull requests, version history, rollback, or revert-to-published when a change needs to be undone |\n\nThe public docs describe the contract and workflow, not private project data. The exact project tool names and schemas are discovered from the selected project MCP at runtime.\n\nInstall manifest\n\nThe public install manifest is:\n\nhttps://mcp.spala.ai/mcp/install-manifest\n\nUse it for current commands, transport, OAuth notes, and the project MCP resolution rule. The rule is always to use the explicit mcpUrl returned by Spala.\n\nCodex Setup\n/agents/codex\nAdd Spala MCP to Codex\nClaude Setup\n/agents/claude\nAdd Spala MCP to Claude Code\nCursor Setup\n/agents/cursor\nConfigure Cursor with Spala MCP\nPublic Agent Skills\n/agents/skills\nPublic skill files for evaluation and safe workflow\nQuick Start\n/getting-started/quickstart\nBuild a backend from the dashboard"
  },
  {
    "path": "/agents/vscode",
    "title": "VS Code and GitHub Copilot Setup",
    "description": "Configure VS Code or GitHub Copilot MCP support to use Spala Public MCP as the discovery and project handoff endpoint.",
    "section": "agents",
    "url": "https://docs.spala.ai/agents/vscode/",
    "last_updated": "2026-07-08",
    "content": "VS Code and GitHub Copilot Setup\n\nUse this MCP endpoint when your VS Code or GitHub Copilot environment supports HTTP MCP servers:\n\nhttps://mcp.spala.ai/mcp\n\nName the server spala-public. The exact JSON file and field names depend on the MCP support available in your VS Code or Copilot build.\n\nSuggested MCP server shape\n\n{\n  \"servers\": {\n    \"spala-public\": {\n      \"type\": \"http\",\n      \"url\": \"https://mcp.spala.ai/mcp\"\n    }\n  }\n}\n\nWorkflow\n\nConfigure the server\n\n    Add spala-public with URL https://mcp.spala.ai/mcp.\n\nAsk for onboarding\n\n    Tell the agent to call spala_get_onboarding and spala_get_tool_map.\n\nAuthenticate\n\n    Complete Spala platform OAuth when project tools are needed.\n\nResolve project MCP\n\n    Use project_list and project_select. Do not type or infer a project MCP URL.\n\nBuild on project MCP\n\n    Let the agent inspect resources, preview changes, validate, publish when requested, and review behavior.\n\nDo not use API URL patterns\n\n  Do not configure https://api.spala.ai/{project}/mcp or any guessed project URL. Project MCP URLs are returned by Spala after auth and project selection.\n\nSpala Public MCP\n/agents/mcp\nRaw MCP notes, OAuth metadata, and handoff flow\nPublic Agent Skills\n/agents/skills\nSkill files for agent evaluation and safe workflow\nStart Here\n/start-here\nChoose the right first path for Spala"
  },
  {
    "path": "/agents/windsurf",
    "title": "Windsurf Setup",
    "description": "Configure Windsurf to use Spala Public MCP for discovery, OAuth handoff, and project MCP resolution.",
    "section": "agents",
    "url": "https://docs.spala.ai/agents/windsurf/",
    "last_updated": "2026-07-08",
    "content": "Windsurf Setup\n\nUse Spala Public MCP as the server URL in Windsurf:\n\nhttps://mcp.spala.ai/mcp\n\nIf your Windsurf build supports HTTP MCP servers, create a server named spala-public with streamable HTTP transport.\n\nInstructions for Windsurf\n\nStart with public Spala MCP.\nCall spala_get_onboarding and spala_get_tool_map.\nUse docs_search before guessing how Spala works.\nAuthenticate through Spala platform OAuth for project tools.\nUse project_select to get the exact project MCP URL.\nWork only on the returned project MCP.\n\nAdd Spala Public MCP\n\n    Configure Windsurf with https://mcp.spala.ai/mcp.\n\nRead public context\n\n    Use onboarding, tool map, docs search, public skills, and machine-readable files before project work.\n\nResolve project access\n\n    Authenticate, list projects, select the correct project, and use the returned project MCP URL.\n\nValidate before publish\n\n    Inspect existing resources and validate changes before publishing.\n\nPublic skills\n\n  Public skills live at https://spala.ai/skills/. They teach agents how to evaluate Spala, get started, hand off frontend work, connect MCP, and use safe workflows.\n\nSpala Public MCP\n/agents/mcp\nDiscovery, OAuth, project selection, and handoff\nPublic Agent Skills\n/agents/skills\nAgent-readable skill files\nFrontend Handoff\n/guides/frontend-handoff\nExact frontend integration packet"
  },
  {
    "path": "/channels/realtime",
    "title": "Realtime Channels",
    "description": "Set up WebSocket connections for live data updates, chat, and collaborative features in Spala.",
    "section": "channels",
    "url": "https://docs.spala.ai/channels/realtime/",
    "last_updated": "2026-07-08",
    "content": "Realtime Channels\n\nRealtime channels provide WebSocket connections for pushing live data to connected clients. Use them for features that require instant updates — chat applications, live notifications, collaborative editing, real-time dashboards, and multiplayer experiences.\n\nCheck availability first\n\n  If Realtime Channels are not visible in your project, use endpoints, triggers, and your frontend realtime provider until Channels are enabled for your workspace.\n\nFrontend handoff requirement\n\n  Realtime is project-specific. Before assigning frontend work, copy the selected channel's WebSocket path, auth mode, permissions, subscribe envelope, event examples, heartbeat interval, close codes, replay behavior, and reconnect expectations into the External Frontend Handoff packet.\n\nSpala Realtime Channels workspace showing channel list, stats, and editor\nRealtime Channels show connected clients, active channels, active subscriptions, and message throughput.\n\nChannel Types\n\n- Database Changes - emit events when selected tables change.\n- Broadcast - send messages to all or selected subscribers.\n- Presence - track who is connected and update clients when presence changes.\n\nHow Realtime Channels Work\n\nUnlike REST endpoints where the client sends a request and waits for a response, WebSocket channels maintain a persistent connection between the client and server. This allows the server to push data to clients at any time without the client having to poll.\n\n1. A client opens a WebSocket connection to a channel\n2. The server authenticates the connection (if configured)\n3. The client subscribes to the channel and optionally to specific topics\n4. The server pushes messages to connected clients when events occur\n5. Clients can also send messages to the server through the same connection\n\nCreating a Realtime Channel\n\nNavigate to the Channels section in the left sidebar\n\nClick New Channel and select Realtime\n\nEnter a channel name (e.g., \"Live Updates\")\n\nSet the path for WebSocket connections (e.g., /ws)\n\nConfigure authentication and event handlers\n\nSave the channel\n\nChannel Configuration\n\nEvent Handlers\n\nRealtime channels support three event handler functions:\n\nOn Connect\n\nRuns when a new client connects to the channel. Use this to initialize client state, join rooms, or send a welcome message.\n\n// On Connect handler\n\n// vars.ws.clientId   → unique connection identifier\n// vars.ws.auth       → decoded JWT payload (if auth is enabled)\n\n// Step 1: Log the connection\n// Step 2: Send welcome message to the client\n// ws.send({ type: \"welcome\", message: \"Connected to live updates\" })\n\nOn Message\n\nRuns when a client sends a message through the WebSocket connection. The message payload is available as vars.ws.message.\n\n// On Message handler\n\n// vars.ws.clientId → who sent the message\n// vars.ws.message  → the parsed message object\n\n// Step 1: Condition - Check message type\n// If vars.ws.message.type === \"chat\"\n\n//   Step 2: Broadcast to all connected clients\n//   ws.broadcast({ type: \"chat\", user: vars.ws.auth.name, text: vars.ws.message.text })\n\nOn Disconnect\n\nRuns when a client disconnects. Use this to clean up resources, update presence status, or notify other clients.\n\n// On Disconnect handler\n\n// Step 1: Database Query - Update user status\n// UPDATE users SET status = 'offline' WHERE id = vars.ws.auth.userId\n\n// Step 2: Broadcast presence update\n// ws.broadcast({ type: \"presence\", userId: vars.ws.auth.userId, status: \"offline\" })\n\nSending Messages\n\nThere are several ways to send messages to connected clients:\n\nFrom a Handler Function\n\nWithin a realtime channel handler, use the WebSocket step actions:\n\n- ws.send(data) — Send a message to the current client only\n- ws.broadcast(data) — Send a message to all connected clients on this channel\n- ws.broadcastToOthers(data) — Send to all clients except the sender\n\nFrom Any Function\n\nAny endpoint, task, or trigger function can push a message to a realtime channel using the Send to Channel step:\n\n// In an endpoint function — notify clients when an order is updated\n\n// Step 1: Database Query - Update the order\n// UPDATE orders SET status = 'shipped' WHERE id = input.orderId\n\n// Step 2: Send to Channel - \"Live Updates\"\n// Message: { type: \"order.updated\", orderId: input.orderId, status: \"shipped\" }\n\n  The Send to Channel step lets any part of your application push realtime updates. This means your REST endpoints can trigger live updates without the client needing to poll.\n\nClient-Side Connection\n\nConnect to a realtime channel from JavaScript using the standard WebSocket API:\n\nconst ws = new WebSocket('wss://<your-project-host>/ws?token=' + encodeURIComponent(token));\n\nws.onopen = () => {\n  console.log('Connected to realtime channel');\n};\n\nws.onmessage = (event) => {\n  const data = JSON.parse(event.data);\n  console.log('Received:', data);\n};\n\nws.onclose = () => {\n  console.log('Disconnected');\n};\n\n  Browser WebSocket clients cannot set arbitrary Authorization headers. Use the authentication method your Spala channel exposes, such as a token query parameter, cookie, or subprotocol.\n\nClient Message Contract\n\nThe exact channel path, auth mode, event names, and payloads are project-specific. Export or copy the generated channel contract before handing realtime work to a frontend developer.\n\nOwner copy workflow:\n\n1. Open Channels in the project dashboard.\n2. Select the channel used by the frontend.\n3. Copy the path, auth mode, access rules, subscribe message, event payload examples, error payloads, heartbeat interval, pong timeout, close-code behavior, replay policy, and reconnect expectation.\n4. Paste those fields into External Frontend Handoff.\n5. If any of those fields are unknown, do not ask the frontend developer to infer them from public docs.\n\nCommon subscribe message:\n\n{\n  \"type\": \"subscribe\",\n  \"channel\": \"cases\"\n}\n\nCommon data event:\n\n{\n  \"type\": \"case.updated\",\n  \"channel\": \"cases\",\n  \"payload\": {\n    \"id\": \"case_123\",\n    \"status\": \"open\",\n    \"updated_at\": \"2026-07-08T00:00:00.000Z\"\n  }\n}\n\nCommon error event:\n\n{\n  \"type\": \"error\",\n  \"code\": \"unauthorized\",\n  \"message\": \"Invalid token\"\n}\n\nIf the channel implements acknowledgements, presence, replay, or reconnect backoff, include examples in the project handoff. If it does not, the frontend should reconnect with exponential backoff and refetch REST state after reconnect.\n\nHeartbeat, Close Codes, and Reconnects\n\nRealtime behavior is configured per project. Include these fields in generated docs or the frontend handoff when the frontend depends on realtime:\n\n| Field | Example |\n| --- | --- |\n| Heartbeat | Server sends { \"type\": \"ping\" }; client replies { \"type\": \"pong\" } |\n| Close codes | 1000 normal close, 1008 auth or policy failure, project-specific validation codes |\n| Acknowledgement | { \"type\": \"ack\", \"id\": \"msg_123\" }, if messages require delivery confirmation |\n| Replay | Cursor, last event id, timestamp window, or no replay |\n| Reconnect | Exponential backoff such as 1s, 2s, 5s, 10s, then max 30s |\n\nIf replay is not implemented, clients should refetch REST state after reconnect. Mobile clients should also refetch after app resume because the socket may have been suspended.\n\nInclude the exact heartbeat interval and timeout in project docs when realtime is part of the frontend contract. For example: server ping every 25 seconds, client pong within 10 seconds, close with 1008 when auth expires, reconnect with exponential backoff up to 30 seconds, then refetch REST state after reconnect.\n\nUse Cases\n\n| Use Case | Channel Messages |\n|---|---|\n| Live chat | Broadcast chat messages to room participants |\n| Notifications | Send targeted messages to specific users |\n| Live dashboard | Push metric updates to all connected dashboard clients |\n| Collaborative editing | Broadcast document changes to all editors |\n| Order tracking | Push status updates to the customer watching their order |\n| Multiplayer games | Sync game state across connected players |\n\nConnection Management\n\nSpala tracks all active WebSocket connections and provides monitoring through the dashboard. You can see the number of connected clients, connection duration, and message throughput. If a client connection drops unexpectedly, the On Disconnect handler fires automatically to clean up.\n\nA descriptive name for the channel\nThe WebSocket endpoint path (e.g., /ws or /ws/chat)\nRequired auth method: None, JWT, or API Key\nFunction that runs when a client connects\nFunction that runs when a client disconnects\nFunction that runs when a message is received from a client\nMaximum simultaneous connections allowed\nREST Channels\n/channels/rest-channels\nGroup REST endpoints with shared configuration\nChannels Overview\n/channels\nLearn about all channel types in Spala\nExternal Frontend Handoff\n/guides/frontend-handoff\nCopy realtime contract details for frontend builders\nBuild Realtime Chat\n/guides/realtime-chat\nStep-by-step guide to building a chat feature"
  },
  {
    "path": "/channels/rest-channels",
    "title": "REST Channels",
    "description": "Group and configure REST API endpoints with shared base paths, authentication, and middleware in Spala.",
    "section": "channels",
    "url": "https://docs.spala.ai/channels/rest-channels/",
    "last_updated": "2026-07-08",
    "content": "REST Channels\n\nREST channels let you group related API endpoints under a shared configuration. All endpoints within a channel inherit the channel's base path, authentication settings, middleware, and rate limits, reducing repetition and keeping your API organized.\n\nCheck availability first\n\n  Most current projects should create endpoints directly in the API section and manage auth on each endpoint. This page is for workspaces where advanced REST Channels are enabled.\n\nCreating a REST Channel\n\nNavigate to the Channels section in the left sidebar\n\nClick New Channel and select REST\n\nEnter a channel name (e.g., \"API v1\")\n\nSet the base path (e.g., /api/v1)\n\nConfigure authentication, middleware, and rate limiting\n\nSave the channel\n\nChannel Settings\n\nBase Path\n\nThe base path is prepended to every endpoint in the channel. If your channel has a base path of /api/v1 and an endpoint with the path /users, the full URL becomes /api/v1/users.\n\nChannel: \"API v1\"\nBase Path: /api/v1\n\nEndpoints:\n  GET  /users        → GET  /api/v1/users\n  POST /users        → POST /api/v1/users\n  GET  /users/:id    → GET  /api/v1/users/:id\n  GET  /products     → GET  /api/v1/products\n\nThis makes API versioning straightforward: create a new v2 channel with the base path /api/v2, copy the endpoints you want to update, and modify them independently.\n\nAuthentication\n\nSet the authentication method at the channel level, and all endpoints within the channel will require it:\n\n- None — No authentication required. Use for public endpoints like health checks and landing page data.\n- JWT — Requires a valid JSON Web Token in the Authorization header. The decoded token payload is available to the endpoint function as vars.auth.\n- API Key — Requires a valid API key in the X-API-Key header or as a query parameter.\n\n// With JWT authentication enabled on the channel:\n// vars.auth.userId  → 42\n// vars.auth.role    → \"admin\"\n// vars.auth.email   → \"user@example.com\"\n\n  Individual endpoints can override the channel's authentication setting. For example, you can set a channel to require JWT but mark a specific health-check endpoint as requiring no authentication.\n\nMiddleware\n\nChannel middleware functions run before every endpoint in the channel. Common uses include:\n\n- Logging — Record request details for all API calls\n- Input sanitization — Clean or validate request bodies\n- Role checking — Verify the authenticated user has the required role\n- Request transformation — Add headers or modify the request body\n\nMiddleware functions run in order. If a middleware function throws an error, the endpoint function does not execute and the error is returned to the client.\n\nRate Limiting\n\nSet a rate limit to restrict how many requests a single client can make per minute. When the limit is exceeded, the server returns a 429 Too Many Requests response.\n\nRate limits are tracked per client IP address by default. With JWT authentication enabled, limits are tracked per authenticated user instead, providing more accurate throttling.\n\n  Rate limits apply per channel. If a client is rate-limited on one channel, they can still make requests to endpoints on other channels.\n\nCORS Configuration\n\nConfigure which origins are allowed to make cross-origin requests to endpoints in this channel. By default, CORS is configured to allow requests from any origin (*). For production, restrict this to your frontend domain(s):\n\n{\n  \"origins\": [\"https://myapp.com\", \"https://admin.myapp.com\"],\n  \"methods\": [\"GET\", \"POST\", \"PUT\", \"DELETE\"],\n  \"headers\": [\"Authorization\", \"Content-Type\"]\n}\n\nOrganizing Your API\n\nA typical Spala project might use channels like this:\n\n| Channel | Base Path | Auth | Purpose |\n|---|---|---|---|\n| Public | / | None | Health check, public content |\n| Auth | /auth | None | Login, register, forgot password |\n| API | /api/v1 | JWT | Protected application endpoints |\n| Admin | /admin | JWT + Role | Admin-only management endpoints |\n| Webhooks | /webhooks | API Key | Incoming webhooks from third parties |\n\nThis structure keeps your API clean, secure, and easy to navigate as it grows.\n\nA descriptive name for the channel\nURL prefix applied to all endpoints (e.g., /api/v1)\nRequired auth method: None, JWT, or API Key\nMaximum requests per minute per client (0 = unlimited)\nAllowed origins for cross-origin requests\nMiddleware functions that run before each endpoint\nRealtime Channels\n/channels/realtime\nWebSocket connections for live data updates\nChannels Overview\n/channels\nLearn about all channel types in Spala\nAPI Endpoints\n/endpoints\nCreate the endpoints that channels group together"
  },
  {
    "path": "/compare/convex",
    "title": "Spala vs Convex",
    "description": "Compare Spala and Convex for AI-built apps, backend automation, reactive data, project handoff, and agent workflows.",
    "section": "compare",
    "url": "https://docs.spala.ai/compare/convex/",
    "last_updated": "2026-07-08",
    "content": "Spala vs Convex\n\nSpala and Convex both help teams build app backends faster, but they are optimized for different jobs.\n\nConvex is a TypeScript-first reactive backend for developers who want app state, backend functions, and database updates to stay synchronized in a Convex-native model.\n\nSpala is a hosted backend builder for AI-built apps. Users describe product intent to AI Copilot, review the generated project in Lite Mode, test endpoints in API Playground, publish the backend API, and hand the exact project contract to a frontend builder or AI coding agent.\n\nChoose Convex when\n\n- Your team wants a TypeScript-native reactive backend.\n- You want backend functions and live queries as the main programming model.\n- Your developers expect to own the app backend in code from day one.\n- You want a Convex-native database and function workflow.\n\nChoose Spala when\n\n- You are turning an AI-built prototype into a real backend.\n- You want database schema, REST APIs, auth, validation, backend logic, docs, and publish checks generated from a product description.\n- You want Lite Mode and the dashboard as an inspect, debug, and customize layer instead of mandatory daily backend coding.\n- You want generated API docs, SDK export, frontend handoff, and MCP-based agent handoff.\n\nMain difference\n\n| Area | Convex | Spala |\n| --- | --- | --- |\n| Primary promise | Reactive backend building blocks | Backend automation for AI-built apps |\n| Main user | TypeScript developers | Founders, builders, agencies, and AI coding agents |\n| Starting point | Write app logic in Convex | Describe the backend feature to AI Copilot |\n| Runtime model | Convex-native functions and data | Generated project API with visual/code editing |\n| Handoff | Developer codebase | API base URL, generated docs/SDK, frontend handoff, project MCP |\n| Dashboard role | Backend/admin dashboard | Optional inspect, debug, and customize layer |\n\nSpala build path\n\n1. Create an account at https://dashboard.spala.ai/signup.\n2. Create or open a project.\n3. Start in Lite Mode.\n4. Ask AI Copilot for the backend feature.\n5. Test endpoints in API Playground.\n6. Publish the backend API.\n7. Share generated API docs or connect an agent through Spala Public MCP.\n\nRelated links\n\n- Create Account\n- Quick Start\n- Lite Mode\n- AI Copilot\n- Frontend Handoff\n- Spala Public MCP\n- Marketing comparison"
  },
  {
    "path": "/database/relationships",
    "title": "Table Relationships",
    "description": "Connect tables with foreign keys for one-to-many and many-to-many patterns.",
    "section": "database",
    "url": "https://docs.spala.ai/database/relationships/",
    "last_updated": "2026-07-08",
    "content": "Table Relationships\n\nRelationships connect your tables so you can link related data — like users to their orders, or products to reviews.\n\nA table schema showing a foreign key field linking to another table\nForeign key fields link tables together\n\nOne-to-many (most common)\n\nOne record in table A can have many related records in table B. The foreign key lives on the \"many\" side.\n\nExample: A user has many orders.\n\nusers\n  id          (primary key)\n  email       (text)\n\norders\n  id          (primary key)\n  user_id     (foreign key -> users.id)\n  total       (number)\n  status      (text)\n\nEach order belongs to one user. Each user can have many orders.\n\nSet up a foreign key\n\nOpen the child table\n\n    In the schema editor, open the table that will hold the reference (the \"many\" side — e.g., orders).\n\nAdd a field\n\n    Add a new field named user_id (convention: singular table name + _id). Set the type to Number.\n\nEnable Foreign Key\n\n    Turn on the Foreign Key option and select the referenced table (e.g., users).\n\nSave\n\n    Click Save. The relationship is now set up.\n\nMany-to-many\n\nWhen both sides can have many related records, create a join table with two foreign keys.\n\nExample: Users can bookmark many products, and products can be bookmarked by many users.\n\nbookmarks\n  id          (primary key)\n  user_id     (foreign key -> users.id)\n  product_id  (foreign key -> products.id)\n\nPro tip\n\n  Join tables can hold extra data too. For example, an order_items table connects orders and products but also stores quantity and price_at_purchase.\n\nCascade options\n\nWhen you delete a record, what happens to its related records?\n\n| Option     | What happens                                           |\n|-----------|-------------------------------------------------------|\n| Restrict  | Prevents deletion if related records exist (default)  |\n| Cascade   | Automatically deletes related records too             |\n| Set Null  | Sets the foreign key to null on related records       |\n\nBe careful with Cascade\n\n  Deleting a user with cascade enabled on orders would also delete all their orders. Use with caution.\n\nQuery related data in functions\n\nIn your functions, you can get related data two ways:\n\n- Joins — Use a Database Query step with a join to get data from both tables in one query.\n- Multiple queries — Fetch the parent first, then query related records in a second step. Often simpler to read.\n\nTables & Fields\n/database/tables\nCreate and configure tables\nFunctions\n/flows\nBuild queries with Database Query steps"
  },
  {
    "path": "/database/tables",
    "title": "Tables & Fields",
    "description": "Create tables, add fields, and manage your database schema in Spala.",
    "section": "database",
    "url": "https://docs.spala.ai/database/tables/",
    "last_updated": "2026-07-08",
    "content": "Tables & Fields\n\nTables hold your data. Each table has fields (columns) that define what data it stores.\n\nCreate a table\n\nOpen the Database section\n\n    Click Database in the left sidebar.\n\nAdd a new table\n\n    Click Add Table and enter a name. Use lowercase with underscores for multi-word names (e.g., order_items).\n\nAdd fields\n\n    Every table starts with an auto-generated id column. Click Add Field to add more.\n\nSave\n\n    Click Save to create the table in your database.\n\nField types\n\n| Type      | Stores                          | Example values                    |\n|-----------|--------------------------------|-----------------------------------|\n| Text      | Strings                        | \"Jane\", \"hello@example.com\"  |\n| Number    | Integers or decimals           | 42, 19.99                     |\n| Boolean   | True or false                  | true, false                   |\n| Date      | Dates and timestamps           | 2025-01-15, now()             |\n| JSON      | Arbitrary structured data      | {\"color\": \"blue\"}              |\n| Enum      | One of a predefined set        | 'pending', 'active'           |\n\nField options\n\n| Option    | What it does                                                  |\n|-----------|--------------------------------------------------------------|\n| Required  | The field must have a value (cannot be empty)                |\n| Unique    | No two rows can have the same value                          |\n| Default   | A value used when none is provided (e.g., 0, 'pending', now()) |\n\nPro tip\n\n  Enable Timestamps on a table to auto-add created_at and updated_at fields. They update automatically.\n\nEdit and delete\n\n- Rename a field — Click the field name in the schema editor and type a new one.\n- Change a type — Select a different type from the dropdown. Be careful with existing data.\n- Delete a field — Click the delete icon next to the field.\n- Delete a table — Use the context menu on the table name. Spala warns when other resources depend on that table.\n\nView and edit data\n\nClick a table, then switch to the Data tab to browse rows, search records, page through larger tables, add test data, edit values inline, or delete rows. This is handy for development, QA, and debugging.\n\nUse the table tabs for the full table workflow:\n\n- Data - Browse, search, edit, import, export, generate, and delete records.\n- Schema - Add fields, change types, review constraints, and update table structure.\n- Triggers - Create logic that runs when records are inserted, updated, or deleted.\n\nCommon table patterns\n\nUsers table:\n\n| Field         | Type    | Options            |\n|--------------|---------|--------------------|\n| email        | Text    | Required, Unique   |\n| password_digest| Text    | Required           |\n| role         | Enum    | Default: 'user'  |\n\nProducts table:\n\n| Field       | Type    | Options           |\n|------------|---------|-------------------|\n| name       | Text    | Required          |\n| price      | Number  | Required          |\n| in_stock   | Boolean | Default: true   |\n| category_id| Number  | Foreign key       |\n\nImport and generate data\n\n- CSV import — Upload a CSV file to bulk-import records into any table. Spala maps columns automatically.\n- CSV export — Download all records from a table as a CSV file.\n- Generate test data — Click Generate Data to create realistic test records for multiple tables at once.\n\nRelationships\n/database/relationships\nConnect tables with foreign keys\nFunctions\n/flows\nQuery your tables in functions\nDatabase Triggers\n/triggers/database-triggers\nRun logic when records change"
  },
  {
    "path": "/deployment/configuration",
    "title": "Project Configuration",
    "description": "Configure project settings, environment variables, addons, AI access, MCP, snapshots, frontend hosting, and API docs.",
    "section": "deployment",
    "url": "https://docs.spala.ai/deployment/configuration/",
    "last_updated": "2026-07-08",
    "content": "Project Configuration\n\nSpala project configuration happens in the dashboard. Use Settings when you need to adjust project behavior, credentials, publishing tools, team access, or agent access.\n\nConfigure In Settings\n\n- General - Project name and project-level metadata\n- Security - Authentication behavior and endpoint protection patterns\n- Users & Roles - Builder access for teammates and reviewers\n- AI - AI data access and Copilot behavior\n- Project Memory - Durable facts, constraints, warnings, and decisions for AI work\n- MCP Server - Project MCP access for authenticated AI agents\n- Frontend App - Static frontend hosting and generated app controls\n- API Docs - Swagger, Markdown, OpenAPI JSON, SDK, and Webflow snippets\n- Environment - Project secrets and environment variables\n- Snapshots - Checkpoints and restore tools\n- CI/CD - External deployment targets and deploy hooks\n\nKeep secrets out of frontend code\n\n  Store integration secrets as project environment variables or addon settings in Spala. Do not expose secret keys in browser code.\n\nSettings\n/settings\nConfigure project-level behavior\nProject Memory\n/features/project-memory\nGive AI durable project context\nAPI Docs\n/features/api-docs\nShare generated API contracts\nSpala Public MCP\n/agents/mcp\nConnect agents through public MCP handoff"
  },
  {
    "path": "/deployment/database",
    "title": "Project Database",
    "description": "Design and use your project database in Spala without managing database infrastructure.",
    "section": "deployment",
    "url": "https://docs.spala.ai/deployment/database/",
    "last_updated": "2026-07-08",
    "content": "Project Database\n\nIn Spala, users manage project data models from the visual Database screen. You create tables, fields, relationships, and database triggers in the dashboard, then use them from endpoints, functions, tasks, agents, channels, and addons.\n\nYou do not need to provision or maintain database infrastructure to use the hosted Spala dashboard.\n\nUse the visual database designer\n\n  Start in Database to add tables, edit data, inspect schema, and create table triggers. Spala handles the managed database layer behind the scenes.\n\nWhat Users Configure\n\n- Tables and fields\n- Relationships between tables\n- Data rows for testing and operations\n- CSV import/export and AI-generated test data\n- Database triggers for insert, update, and delete events\n- Data access from functions, endpoints, tasks, and project agents\n\nDatabase\n/database\nCreate tables, fields, and relationships visually\nTables & Fields\n/database/tables\nDesign your project schema and data\nDatabase Triggers\n/triggers/database-triggers\nRun logic when records change\nDatabase Steps\n/reference/steps/database\nUse project data inside functions"
  },
  {
    "path": "/deployment/publish",
    "title": "Publish",
    "description": "Publish generated Spala project changes as a live API from the dashboard.",
    "section": "deployment",
    "url": "https://docs.spala.ai/deployment/publish/",
    "last_updated": "2026-07-08",
    "content": "Publish\n\nSpala users publish project changes from the dashboard. Publishing applies your project draft and updates the generated API your frontend, integrations, and agents call.\n\nUse Publish for your project\n\n  The deployment action users normally need is Publish. It makes your generated project API live without requiring a Spala platform deployment runbook.\n\nPublish Flow\n\nBuild or edit your backend\n\n    Use AI Copilot, Lite mode, or Advanced mode to create tables, functions, endpoints, tasks, triggers, project agents, channels, and settings.\n\nReview the project\n\n    Inspect the project graph, endpoint list, API Playground, generated docs, snapshots, and pending changes.\n\nPublish\n\n    Click Publish in the dashboard. Spala applies the project changes and updates the live API.\n\nUse the API\n\n    Connect your frontend, use generated endpoint snippets, or let an authenticated AI agent continue work through the selected project MCP.\n\nExternal Deployment\n\nIf your team runs the generated project artifact in another environment, use CI/CD settings to connect deployment hooks or server targets. That is still project deployment, not a Spala platform runbook.\n\nPublish\n/features/publish-export\nReview and publish project changes\nCI/CD\n/features/ci-cd\nConnect external project deployment targets\nAPI Playground\n/features/playground\nTest endpoints before and after publishing\nSpala Public MCP\n/agents/mcp\nLet agents discover and hand off to the right project MCP"
  },
  {
    "path": "/deployment/start",
    "title": "Start a Project",
    "description": "Create a Spala project from the hosted dashboard and publish the generated backend.",
    "section": "deployment",
    "url": "https://docs.spala.ai/deployment/start/",
    "last_updated": "2026-07-08",
    "content": "Start a Project\n\nStart from the hosted dashboard, then use AI Copilot, Lite mode, or Advanced mode to build the backend you need.\n\nHosted dashboard\n\n  New users should open https://dashboard.spala.ai/signup. Existing users can sign in from the dashboard and create or open a project.\n\nFirst Project\n\nOpen the dashboard\n\n    Create an account at https://dashboard.spala.ai/signup, or sign in if you already have one.\n\nCreate a project\n\n    Click New Project and name the backend or app you want to build.\n\nDescribe what you need\n\n    Use AI Copilot in Lite mode to describe the feature, API, database, or app workflow you want.\n\nInspect the generated project\n\n    Review tables, functions, endpoints, agents, tasks, triggers, settings, and API docs before publishing.\n\nPublish when ready\n\n    Use Publish to make the generated API available to your frontend, integrations, or AI agents.\n\nQuick Start\n/getting-started/quickstart\nBuild a simple API in the dashboard\nAI Copilot\n/features/ai-assistant\nUse plain language to generate project resources\nLite Mode\n/features/lite-mode\nWork from the Copilot-first project overview\nThe Interface\n/getting-started/interface\nLearn the dashboard layout"
  },
  {
    "path": "/endpoints/creating-endpoints",
    "title": "Create an Endpoint",
    "description": "Step-by-step guide to creating a REST API endpoint in Spala.",
    "section": "endpoints",
    "url": "https://docs.spala.ai/endpoints/creating-endpoints/",
    "last_updated": "2026-07-08",
    "content": "Create an Endpoint\n\nEndpoints connect HTTP routes to functions. When a client sends a request, Spala runs the attached function and returns the result.\n\nStep by step\n\nOpen the Endpoints section\n\n    Click Endpoints in the left sidebar.\n\nClick Add Endpoint\n\n    Click Add Endpoint to open the endpoint editor.\n\nChoose the HTTP method\n\n    Pick the method that matches what the endpoint does:\n\n    | Method | When to use                     |\n    |--------|---------------------------------|\n    | GET    | Read data                       |\n    | POST   | Create something new            |\n    | PUT    | Replace or update something     |\n    | PATCH  | Partially update something      |\n    | DELETE | Remove something                |\n\nSet the URL path\n\n    Enter the path. Use :paramName for dynamic segments:\n\n    /api/cases\n    /api/cases/:id\n    /api/users/:userId/cases\n\n    Dynamic segments become available as params.id, params.userId, etc. in your function.\n\nAttach a function\n\n    Select the function that handles this endpoint. Create the function first if needed.\n\nSet authentication\n\n    Choose None for public endpoints, or JWT to require a login token. See Protect Your Endpoints.\n\nSave\n\n    Click Save. Your endpoint is saved in the project draft and is ready to test in the Playground. Publish when you want live clients to use it.\n\nThe endpoint editor\nConfigure method, path, function, and authentication\n\nURL path tips\n\nUse plural nouns for collections and nest related resources:\n\n/products            -- all products\n/products/:id        -- one product\n/products/:id/reviews  -- reviews for a product\n\nFor actions that are not standard CRUD, use descriptive paths:\n\nPOST /api/auth/login\nPOST /orders/:id/cancel\nPOST /api/cases/:id/status\n\nGood to know\n\n  Path parameter values are always strings. To use them as numbers (e.g., for a database query), convert with params.id | toNumber.\n\nManaging endpoints\n\n- Edit — Click any endpoint in the list to change its settings.\n- Duplicate — Right-click an endpoint to copy it. Useful for creating similar routes quickly.\n- Delete — Right-click and delete. The attached function is not affected.\n- Generate description — Ask AI to draft a clear endpoint summary for docs and review.\n- Requests — Open request history to inspect real calls, timing, output, and failures.\n- Docs visibility — Hide internal routes from generated API docs when they are not meant for clients.\n\nInputs & Outputs\n/endpoints/inputs-outputs\nHandle request data and configure responses\nProtect Your Endpoints\n/endpoints/authentication\nAdd JWT authentication"
  },
  {
    "path": "/endpoints/inputs-outputs",
    "title": "Inputs & Outputs",
    "description": "Handle path parameters, query strings, request bodies, and configure API responses.",
    "section": "endpoints",
    "url": "https://docs.spala.ai/endpoints/inputs-outputs/",
    "last_updated": "2026-07-08",
    "content": "Inputs & Outputs\n\nYour endpoints receive data from requests and send back responses. Define the inputs an endpoint expects, attach a function, then use those values from the function steps.\n\nThe endpoint editor showing input and output configuration\nConfigure inputs and outputs in the endpoint editor\n\nInputs\n\nWhen a request arrives, Spala makes the data available through built-in variables:\n\nPath parameters (params)\n\nDynamic segments in the URL path:\n\n// Endpoint: GET /api/cases/:id\n// Request:  GET /api/cases/42\n\nparams.id  // \"42\"\n\nQuery string (query)\n\nKey-value pairs after the ? in the URL:\n\n// Request: GET /api/cases?page=2&limit=10\n\nquery.page   // \"2\"\nquery.limit  // \"10\"\n\nGood to know\n\n  Query string and path parameter values are always strings. Convert them with query.page | toNumber when you need a number.\n\nRequest body (input)\n\nThe JSON body for POST, PUT, and PATCH requests:\n\n// Request: POST /api/cases\n// Body: { \"case_name\": \"Contract Review\", \"status\": \"open\" }\n\ninput.case_name  // \"Contract Review\"\ninput.status     // \"open\"\n\nHeaders (headers)\n\nHTTP headers sent with the request:\n\nheaders.authorization    // \"Bearer eyJhbG...\"\nheaders['content-type']  // \"application/json\"\n\nFile inputs\n\nFor upload endpoints, add a file input in the endpoint editor and use the generated input in the attached function. This keeps the Playground, generated docs, and implementation in sync.\n\nOutputs\n\nUse a Return step in your function to set the response:\n\n// Return data with a 200 status\nValue:  vars.cases\nStatus: 200\n\n// Return an error with a 404 status\nValue:  { error: \"Case not found\" }\nStatus: 404\n\n// Return a created resource with 201\nValue:  vars.newCase\nStatus: 201\n\nCommon status codes\n\n| Code | Meaning          | When to use                        |\n|------|------------------|------------------------------------|\n| 200  | OK               | Successful read or update          |\n| 201  | Created          | Successfully created a resource    |\n| 400  | Bad Request      | Missing or invalid input           |\n| 401  | Unauthorized     | Missing or bad authentication      |\n| 404  | Not Found        | Resource does not exist            |\n| 500  | Server Error     | Something went wrong               |\n\nCreate an Endpoint\n/endpoints/creating-endpoints\nSet up your endpoints\nAPI Docs\n/features/api-docs\nShare endpoint inputs and responses\nProtect Your Endpoints\n/endpoints/authentication\nAdd authentication"
  },
  {
    "path": "/endpoints/authentication",
    "title": "Protect Your Endpoints",
    "description": "Add authentication to your API endpoints with JWT tokens and role-based access.",
    "section": "endpoints",
    "url": "https://docs.spala.ai/endpoints/authentication/",
    "last_updated": "2026-07-08",
    "content": "Protect Your Endpoints\n\nNot every endpoint should be public. You can require authentication so only logged-in users (or users with specific roles) can access certain routes.\n\nThe endpoint editor with the JWT authentication option\nEnable JWT authentication on any endpoint\n\nRequire authentication\n\nOpen the endpoint\n\n    Go to Endpoints and click the endpoint you want to protect.\n\nSet authentication to JWT\n\n    In the endpoint editor, change Authentication from None to JWT.\n\nSave\n\n    Click Save. The endpoint now requires a valid token in the Authorization header.\n\nClients must include the token like this:\n\nAuthorization: Bearer eyJhbGciOiJIUzI1NiJ9...\n\nIf the token is missing or invalid, Spala returns a 401 Unauthorized response automatically.\n\nHow JWT tokens work\n\nJWT (JSON Web Tokens) is a simple way to handle login sessions:\n\n1. A user logs in with their email and password via a public /api/auth/login endpoint\n2. Your login function verifies the credentials and creates a token using a Generate Auth Token step\n3. The client stores the token and sends it with every request\n4. Protected endpoints automatically verify the token before running the function\n\nGood to know\n\n  Tokens contain user data (like id, email, role) and expire after a set time (e.g., 24 hours). No database lookup is needed to verify them.\n\nFrontend session contract\n\nFrontend builders need the exact session contract generated for the project. At handoff time, document:\n\n| Field | Example |\n| --- | --- |\n| Login route | POST /api/auth/login |\n| Signup route | POST /api/auth/register |\n| Token field | token, accessToken, or data.token |\n| Protected header | Authorization: Bearer USER_JWT |\n| Expiry | 24h, 7d, or project-specific |\n| Refresh route | POST /api/auth/refresh, if implemented |\n| Logout behavior | Clear local token, revoke server session, or both |\n| Cookie mode | credentials: \"include\" if the project uses cookie sessions |\n| CSRF header | Required only when the project implements CSRF protection |\n\nIf no refresh route is documented, treat token expiry as a login-required event. If the project uses cookie sessions, browser CORS must also allow credentials and the frontend must use credentials: \"include\".\n\nCookie-session request example:\n\nconst response = await fetch(`${SPALA_API_BASE_URL}/api/me`, {\n  credentials: \"include\",\n  headers: {\n    \"X-CSRF-Token\": csrfToken,\n  },\n});\n\nThe CSRF header name, cookie attributes, same-site policy, and logout behavior are project-specific. Document them in the generated API docs or frontend handoff packet before asking an external frontend developer to integrate cookie auth.\n\nCookie checklist for browser clients:\n\n| Field | What to confirm |\n| --- | --- |\n| SameSite | Lax, Strict, or None; cross-site cookies require SameSite=None |\n| Secure | Required for cross-site HTTPS cookies |\n| Domain/path | Which frontend and API hosts receive the cookie |\n| CSRF source | Meta tag, cookie, endpoint, or generated page value |\n| Logout | Whether logout expires the cookie, revokes the server session, or both |\n\nBuilt-in endpoint auth vs manual verification\n\nUse endpoint-level JWT authentication when an endpoint should be protected before its function runs. This is the usual production path for user-facing APIs because Spala rejects missing or invalid tokens automatically and exposes decoded auth data to the function.\n\nUse a manual Verify JWT step inside a function only when the endpoint intentionally accepts mixed public/authenticated traffic, needs to verify a secondary token, or is implementing a custom auth handshake such as a realtime connection or webhook signature.\n\nAccess the logged-in user in a function\n\nWhen an endpoint has JWT authentication enabled, the decoded token data is available in your function:\n\nvars.auth.userId   // The user's ID\nvars.auth.email    // The user's email\nvars.auth.role     // The user's role, such as \"admin\" or \"user\"\n\nUse this to personalize responses or restrict actions:\n\n// Only return the current user's cases\nDatabase Query: SELECT * FROM cases WHERE client_id = vars.auth.userId\n\nAdd role-based access\n\nFor admin-only endpoints, add a role check at the start of your function:\n\n// Step 1: Check role\nIf/Else: vars.auth.role !== 'admin'\n  True branch: Return { error: \"Admin access required\" }, Status: 403\n\n// Step 2: Continue with admin logic...\n\nWhich endpoints to protect?\n\n| Endpoint              | Auth   | Why                              |\n|----------------------|--------|----------------------------------|\n| POST /api/auth/login   | None   | Users need to log in             |\n| POST /api/auth/register| None   | Users need to sign up            |\n| GET /products      | None   | Anyone can browse                |\n| POST /products     | JWT    | Only logged-in users can create  |\n| DELETE /products/:id| JWT   | Only admins can delete           |\n| GET /orders        | JWT    | Users see only their own orders  |\n\nNever store plain-text passwords\n\n  Always use the Hash Password step (bcrypt) before saving passwords, and Verify Password when verifying during login.\n\nCreate an Endpoint\n/endpoints/creating-endpoints\nSet up endpoints step by step\nInputs & Outputs\n/endpoints/inputs-outputs\nHandle request data\nUser Authentication Guide\n/guides/user-auth\nFull login and registration setup"
  },
  {
    "path": "/features/ai-assistant",
    "title": "AI Copilot",
    "description": "The fastest way to build with Spala — describe what you need in plain English and the AI builds your entire backend.",
    "section": "features",
    "url": "https://docs.spala.ai/features/ai-assistant/",
    "last_updated": "2026-07-08",
    "content": "AI Copilot\n\nThe fastest way to build with Spala. Describe what you need in plain English and the AI creates your database tables, functions, and API endpoints in seconds.\n\nThis is how most people build with Spala\n\n  Skip the step-by-step setup. Tell the AI Copilot what you want, review the changes, and apply.\n\nThe AI Copilot panel open in Build mode showing context tabs and prompt area\nOpen the AI Copilot (⌘J) and describe what you want to build\n\nBuild a complete feature in 30 seconds\n\nDescribe what you need\n\n    Open the AI Copilot with ⌘J (or click the AI button in the header) and type what you want. Be as specific as you like:\n\n    > \"I need a booking system where users can reserve time slots, cancel bookings, and get email confirmations. Each booking has a date, time, duration, and status.\"\n\n    The AI understands your intent and figures out the right tables, functions, and endpoints.\n\nReview the diff\n\n    The AI generates a complete set of changes and shows them as a diff, exactly like a pull request. You see every table, function, and endpoint that will be created or modified. Nothing changes until you approve it.\n\nApply with one click\n\n    Click Apply to add the feature to your project draft: tables created, functions wired up, endpoints ready to test. Test it in the Playground, then publish when you are ready. If Autopublish is enabled, Spala can publish after applying.\n\nWhat the AI Copilot creates for you\n\nA single prompt can generate:\n\n- Database tables with the right fields, types, and relationships\n- Functions with queries, conditions, validation, and error handling\n- API endpoints with proper methods, paths, and authentication\n- Addon integrations like email notifications or payment processing\n\nThe AI Copilot showing a diff of proposed changes with tables and endpoints\nThe AI generates a full diff — review every change before applying\n\nContext tabs\n\nThe AI Copilot shows your project context across several tabs at the top of the panel:\n\n- General — Project-wide settings and overview\n- Data — Database tables and relationships the AI considers\n- Functions — Existing backend logic the AI can reference or extend\n- API — Current endpoints and routes\n- Tasks — Scheduled and background tasks\n- Triggers — Database change triggers and event handlers\n\nThese tabs let you see exactly what context the AI has when generating changes. The AI reads your entire project structure to produce accurate, compatible output.\n\nAdditional Context\n\nClick Additional Context (below the context tabs) to add custom instructions that are included in every AI request. Use this for:\n\n- Coding conventions (\"Always add created_at and updated_at timestamps\")\n- Business rules (\"All prices should be stored in cents\")\n- Tech stack preferences (\"Use the email addon for notifications\")\n\nThe Additional Context text area expanded in the AI Copilot panel\nAdd custom context that the AI includes in every request\n\nCopilot settings\n\nClick the ⋮ menu in the copilot header to configure:\n\n| Setting | What it does |\n|---|---|\n| PRO mode | Uses a more capable AI model for complex prompts |\n| Autopublish | Automatically publishes changes after applying (skips the manual publish step) |\n| Test with AI | Has the AI review its own generated changes for quality before you apply |\n\nYou can also toggle PRO mode and Test with AI directly from the badges below the context tabs.\n\nWrite better prompts\n\nThe more context you give, the better the result:\n\n| Good prompt | Why it works |\n|---|---|\n| \"A tasks table with title, status, priority, due_date, and assigned_to (references users)\" | Specifies fields and relationships |\n| \"When a task is marked complete, send an email to the project owner\" | Describes behavior, not implementation |\n| \"GET /api/tasks should support filtering by status and sorting by due_date\" | Specifies API contract |\n| \"If the time slot is already booked, return a 409 error\" | Includes edge cases |\n\nAI Copilot + manual editing\n\nThe AI Copilot is your starting point, not a replacement for the visual editor. A typical workflow:\n\n1. AI Copilot creates the feature scaffolding (tables, functions, endpoints)\n2. Visual editor lets you fine-tune individual steps and conditions\n3. Code view gives you full JavaScript control when you need it\n\nYou can also use the AI Copilot to modify existing features: \"Add pagination to the list products endpoint\" or \"Add a discount_code field to the orders table.\"\n\nWhen to use what\n\n| Start with AI Copilot | Build manually |\n|---|---|\n| New features from scratch | Fine-tuning an existing function |\n| Standard patterns (CRUD, auth, search) | Complex custom business logic |\n| Rapid prototyping | Precise control over every step |\n| Setting up database + endpoints together | Working with niche addon configurations |\n\nAsk questions about your project\n\nThe AI Copilot in Ask mode for querying your project\nSwitch to Ask mode to chat about your project\n\nSwitch to Ask mode to chat with the AI about your project. Ask things like:\n- \"Which endpoints use the orders table?\"\n- \"How does the login flow work?\"\n- \"What addons are installed?\"\n\nThe AI searches across your entire project to give you accurate, referenced answers.\n\nAI Data Access\n\nBy default, Ask mode only sees your project structure (tables, functions, endpoints). To let the AI query actual data from your database, enable AI Data Access in Settings > AI.\n\nThe AI Data Access settings page showing table access configuration\nConfigure which tables and fields the AI can query in Ask mode\n\nWith Data Access enabled, you can ask questions like:\n- \"How many users signed up this week?\"\n- \"Show me the last 10 orders with status 'pending'\"\n- \"What is the average order value?\"\n\nYou control exactly which tables and fields the AI can access:\n\n- Enable/disable per table — Toggle access for each table individually\n- Field-level control — Select which fields the AI can see (exclude sensitive columns like password hashes)\n- Max rows per table — Limit how many rows the AI can query (default: 20)\n- Allow Aggregates — Enable COUNT, SUM, AVG, MIN, MAX queries\n\n  AI Data Access uses read-only SELECT queries only. The AI cannot modify, insert, or delete data. Review which fields you expose — avoid granting access to columns with sensitive information.\n\nQuality review\n\nAfter the AI generates changes, toggle Test with AI to have it review its own work. The AI:\n- Scores each generated item for quality\n- Identifies gaps (missing validation, error handling, edge cases)\n- Suggests fixes you can apply with one click\n\nThis catches issues before you apply changes to your project.\n\nQuick Start\n/getting-started/quickstart\nBuild your first API step by step\nLite Mode\n/features/lite-mode\nUse the Copilot workspace and project graph\nAddons\n/addons\nAdd payments, email, AI, and more to your functions"
  },
  {
    "path": "/features/api-docs",
    "title": "API Docs",
    "description": "Share generated endpoint docs, OpenAPI, SDK files, and snippets with humans or AI frontend builders.",
    "section": "features",
    "url": "https://docs.spala.ai/features/api-docs/",
    "last_updated": "2026-07-08",
    "content": "API Docs\n\nAPI Docs turns your Spala endpoints into shareable contracts. Use it when a frontend developer, AI coding agent, or external integration needs to know what routes exist and how to call them.\n\nAPI Docs cover REST\n\n  Generated API Docs, OpenAPI JSON, and SDK files are the source of truth for REST endpoints. They may not describe realtime channel protocol, direct-to-storage upload CORS, required signed-upload PUT headers, cookie/CSRF details, refresh/logout behavior, OAuth callback URLs, or mobile app resume behavior. Include those details in the External Frontend Handoff when the project uses them.\n\nWhat You Can Share\n\nSwagger UI\nInteractive browser explorer for testing live endpoints.\n/features/api-docs\n\nMarkdown Docs\nReadable endpoint documentation that works well as AI context.\n/features/api-docs\n\nOpenAPI JSON\nMachine-readable API contract for tools and clients.\n/features/api-docs\n\nSDK\nGenerated JavaScript or TypeScript client helpers for your published API.\n/features/api-docs\n\nWebflow Snippets\nEmbed-ready snippets for no-code or lightweight frontend pages.\n/features/api-docs\n\nGenerated Artifact Routes\n\nSettings → API Docs is the easiest way to copy or export the contract. Authenticated project tooling can also request these generated routes from the project base URL:\n\n| Artifact | Route | Notes |\n| --- | --- | --- |\n| Markdown docs | GET {{PROJECT_BASE_URL}}/api/__internal/docs | Builder-authenticated source for humans and AI agents |\n| JSON docs | GET {{PROJECT_BASE_URL}}/api/__internal/docs?format=json | Structured project docs with full URLs |\n| OpenAPI JSON | GET {{PROJECT_BASE_URL}}/api/__internal/docs/openapi.json | Use for API clients, Swagger, and codegen |\n| TypeScript SDK | GET {{PROJECT_BASE_URL}}/api/__internal/docs/sdk.ts | Generated client helpers for the current project |\n| Public Markdown docs | GET {{PROJECT_BASE_URL}}/api/docs | Available only when public API docs are enabled |\n| Public JSON docs | GET {{PROJECT_BASE_URL}}/api/docs?format=json | Available only when public API docs are enabled |\n| Swagger UI | Settings → API Docs | Dashboard UI; use a direct route only if the selected project explicitly exposes one |\n\nIf the generated docs show a base URL with a scoped path, keep it exactly as shown. Do not derive or shorten project URLs from the project name.\n\nWhy It Matters\n\nGenerated docs reduce the gap between backend and frontend work. Instead of explaining routes manually, you can share the current API contract, let an AI frontend builder read it, and keep implementation details tied to the project state.\n\nUse docs before frontend work\n\n  After publishing backend changes, open Settings > API Docs and share the Markdown, OpenAPI JSON, or SDK with the person or agent building the UI.\n\nRecommended Workflow\n\n1. Build or update endpoints in Spala.\n2. Test them in the Playground.\n3. Publish the backend.\n4. Open Settings > API Docs.\n5. Share the best format for the consumer: Markdown for AI, Swagger for manual testing, OpenAPI for tooling, SDK for app code.\n6. Add separate handoff details for realtime, signed uploads, cookies/CSRF, refresh/logout, mobile callbacks, and CORS when those features are used.\n\nPublic Docs Switch\n\nThe internal generated docs require builder authentication. Public GET /api/docs is available only when public API docs are enabled in project security settings. Leave public docs off for private APIs unless you intentionally want unauthenticated consumers to read the contract.\n\nAPI Playground\n/features/playground\nTest endpoints before sharing docs\nFrontend App\n/features/frontend-app\nHost the app UI that consumes the API\nAPI Endpoints\n/endpoints\nCreate the routes that appear in generated docs\nExternal Frontend Handoff\n/guides/frontend-handoff\nCopy the complete packet for frontend builders"
  },
  {
    "path": "/features/playground",
    "title": "API Playground",
    "description": "Test your endpoints right in the browser — no Postman or curl needed.",
    "section": "features",
    "url": "https://docs.spala.ai/features/playground/",
    "last_updated": "2026-07-08",
    "content": "API Playground\n\nTest your endpoints right in the browser — no Postman or curl needed.\n\nThe API Playground ready to send a request\nSelect an endpoint, set parameters, and send a request to see the response.\n\nHow to use the Playground\n\nSelect an endpoint\n\n    Open the Playground screen and pick an endpoint from the dropdown. The method, path, and any path parameters are filled in automatically.\n\nSet your request\n\n    Add path parameters, query parameters, authentication, body fields, or file uploads depending on what your endpoint expects.\n\nSend and inspect\n\n    Click Send. The response appears below with status, timing, output, and logs so you can see exactly what your endpoint returned.\n\nPro tip\n\n  The Playground auto-fills your endpoint's path and method. Just add your data and send.\n\nTesting authenticated endpoints\n\nFor endpoints protected with JWT authentication, use the Playground authentication field:\n\n1. First, call your login endpoint to get a token\n2. Copy the token from the response\n3. Paste the token into the authentication field\n4. Now you can test any protected endpoint\n\nGood to know\n\n  Draft or modified endpoints may need to be published before the Playground can call the live route.\n\nWhat To Check\n\n- Required path parameters are filled in\n- Query parameters are converted to the types your function expects\n- Auth tokens are present for protected endpoints\n- File uploads match the endpoint input shape\n- Logs explain failures before you publish or connect a frontend\n\nBuild a REST API\n/guides/build-rest-api\nCreate endpoints to test\nAdd User Authentication\n/guides/user-auth\nSet up JWT tokens for protected endpoints\nPublish\n/features/publish-export\nPublish your tested endpoints"
  },
  {
    "path": "/features/ci-cd",
    "title": "CI/CD",
    "description": "Configure deploy hooks or approved project artifact delivery after publishing.",
    "section": "features",
    "url": "https://docs.spala.ai/features/ci-cd/",
    "last_updated": "2026-07-08",
    "content": "CI/CD\n\nCI/CD connects a published Spala project backend to a deployment workflow. It can trigger external deploy hooks or deliver approved project artifacts when enabled for the account. It does not grant Spala dashboard, builder, or platform source code. Verify runtime packaging, portability, self-hosting, support, and migration terms before relying on an external deployment path.\n\nGenerated Project Artifact\n\nThe generated project artifact is the backend artifact for one Spala project. It represents the published project API behavior: route handlers, function logic, runtime configuration, docs metadata, and the environment-variable contract needed by that project. It is not the Spala dashboard, visual builder, platform auth system, or Spala source repository.\n\nHosted Publish is the normal path for most users. CI/CD is the advanced path when your team wants the generated project artifact delivered to your configured deployment target when enabled for your account or deploy provider. Feature availability depends on the generated project artifact package and the target environment you configure, so test the target with dry run before treating it as production.\n\nDelivery Boundary\n\n| Action | What changes | Where it runs |\n| --- | --- | --- |\n| Publish backend API | Hosted project API behavior becomes live | Spala-managed project API |\n| Publish frontend app | Static UI assets become live | Spala frontend hosting for the project |\n| Deploy generated project artifact | Generated project runtime is delivered outside the default hosted publish path | Your server or external deploy provider |\n\nSupported Target Types\n\n- SSH server\n- SFTP server with SSH commands\n- Vercel deploy hook\n- Render deploy hook\n- Netlify deploy hook\n- Railway deploy hook\n- Custom webhook\n\nRecommended Workflow\n\nPublish the backend\n\n    First publish the project so the generated backend state is ready.\n\nAdd a deployment target\n\n    Open Settings > CI/CD, choose a provider, and enter the target credentials or deploy hook URL.\n\nRun a dry run\n\n    Use dry run to check the bundle and target configuration before deploying.\n\nDeploy\n\n    Deploy manually, or enable auto-deploy after publish when the target is stable.\n\nProject deployment only\n\n  CI/CD is for deploying your generated project backend or triggering your configured deployment target when enabled for your account. It is not a guide for running the Spala dashboard or platform.\n\nWhy Users Care\n\nSome teams want Spala to build and validate the backend while their production deployment workflow remains in their configured cloud or deployment workflow. CI/CD keeps that path explicit and repeatable.\n\nPublish\n/features/publish-export\nPublish project changes before deployment\nProject Snapshots\n/features/project-snapshots\nCreate checkpoints before large deploys\nEnvironment Variables\n/settings\nStore deploy and runtime values safely"
  },
  {
    "path": "/features/editor-workspace",
    "title": "Editor Workspace",
    "description": "Use tabs, search, view modes, Ask AI, Try, compare, save, version history, and run history while editing Spala resources.",
    "section": "features",
    "url": "https://docs.spala.ai/features/editor-workspace/",
    "last_updated": "2026-07-08",
    "content": "Editor Workspace\n\nThe editor workspace is where direct resource editing happens. Open tables, functions, endpoints, tasks, triggers, and channels from the sidebar, search palette, project graph, or overview cards.\n\nSpala function editor with Gherkin, Visual, Split, and Code view modes\nFunction editors can switch between readable, visual, split, and code views.\n\nOpen Resources As Tabs\n\nSpala keeps resources open as editor tabs so you can move between related work without losing context.\n\n- Pin important tabs so they stay open\n- Drag tabs to reorder your working set\n- Close one tab, tabs to the left or right, or every other tab\n- Use the modified indicator to spot unsaved work\n- Return to recently opened resources from search\n\nEditor Header Actions\n\nView Modes\nSwitch between Gherkin, Visual, Split, and Code when the resource supports them.\n/flows/code-view\n\nRemember View\nSave your preferred view mode so future resources open the way you work.\n/getting-started/interface\n\nAsk AI\nAsk the Copilot about the resource or request a targeted change.\n/features/ai-assistant\n\nTry\nRun an endpoint, function, task, or supported resource with sample input.\n/flows/debugging\n\nCompare\nReview pending changes before saving or publishing.\n/features/pull-requests\n\nSave Controls\nSave, discard, or revert unpublished changes from the change bar.\n/features/publish-export\n\nSpala Code view showing JavaScript for a backend function\nCode view is available when you need direct JavaScript control.\n\nResource Menu\n\nThe resource menu exposes actions that are useful during review and recovery:\n\n- Edit - rename or adjust resource metadata\n- Versions - inspect resource history\n- Runs - open run and execution history\n- Export JSON - download the resource definition for review or support\n- Revert to Published - discard draft changes and return to the live version\n- Delete - remove the resource, with dependency checks where needed\n\nSearch Palette\n\nPress Cmd+K or Ctrl+K to open search. Search covers tables, functions, endpoints, tasks, triggers, and channels. When the query is empty, Spala shows recent resources first.\n\n| Prefix | Searches |\n|--------|----------|\n| @t, @table | Tables |\n| @f, @fn, @function | Functions |\n| @e, @endpoint | Endpoints |\n| @tk, @task | Tasks |\n| @tr, @trigger | Triggers |\n| @ch, @channel | Channels |\n\nUse search for speed\n\n  The fastest way to jump into a known resource is usually Cmd+K or Ctrl+K, then a type prefix and part of the name or route.\n\nThe Interface\n/getting-started/interface\nLearn the main Spala screens\nProject Overview\n/features/project-overview\nInspect the whole backend before opening editors\nAI Copilot\n/features/ai-assistant\nAsk for changes from the editor\nTry & Runs\n/flows/debugging\nRun resources and inspect execution output"
  },
  {
    "path": "/features/frontend-app",
    "title": "Frontend App",
    "description": "Publish a static frontend UI to your project URL, preview it first, and roll back if needed.",
    "section": "features",
    "url": "https://docs.spala.ai/features/frontend-app/",
    "last_updated": "2026-07-08",
    "content": "Frontend App\n\nFrontend App lets you host a static user interface next to the backend you built in Spala. Use it when your project needs a simple deployed app UI without wiring another hosting service first.\n\nWhat It Helps With\n\n- One project URL - keep the backend API and app UI connected to the same Spala project.\n- Preview before frontend publish - upload a build, inspect the preview URL, then publish the frontend app only when it looks right.\n- Agent handoff - copy instructions for an AI coding agent so it can build and upload the frontend package.\n- Rollback - restore the previous frontend app if the new upload is not correct.\n- Cleanup - remove old staged uploads after review.\n\nPublish A Frontend App\n\n Frontend App\n    Use this screen when you already have a static build ZIP or want to give an agent upload instructions.\n\nUpload or use agent mode\n\n    Upload a ZIP manually, or copy the agent instruction and let an AI coding tool prepare the build.\n\nPreview\n\n    Spala validates the upload and returns a preview URL. Check routing, API calls, login behavior, and mobile layout before publishing.\n\nPublish frontend app\n\n    Click Publish in Frontend App to make the staged UI live for the project. This publishes static frontend assets; backend API changes still use Publish backend API.\n\nDelivery Boundary\n\n| Action | What changes | Where it lives |\n| --- | --- | --- |\n| Publish backend API | Tables, functions, endpoints, tasks, triggers, agents, channels, settings, and generated API behavior | Project API base URL |\n| Publish frontend app | Static UI files uploaded through Frontend App | Project frontend URL |\n| Deploy generated project artifact | Generated project runtime sent to an external target | Your server or deploy provider |\n\nBackend first, frontend next\n\n  Spala is still the source of truth for your backend: tables, auth, endpoints, functions, tasks, triggers, docs, and MCP. Frontend App gives that backend a hosted UI when you need one.\n\nWhen To Use It\n\nUse Frontend App for product demos, internal tools, client portals, and AI-generated app UIs that call your Spala endpoints. If you already host your frontend on Vercel, Netlify, or another provider, you can keep doing that and use Spala only for the backend API.\n\nAPI Docs\n/features/api-docs\nGive frontend builders the generated API contract\nPublish\n/features/publish-export\nPublish backend changes before connecting the frontend\nMCP\n/agents/mcp\nLet agents resolve the right project MCP before working"
  },
  {
    "path": "/features/lite-mode",
    "title": "Lite Mode",
    "description": "Use Spala's Copilot workspace to inspect a backend, ask for changes, and jump into focused tools without opening the full builder.",
    "section": "features",
    "url": "https://docs.spala.ai/features/lite-mode/",
    "last_updated": "2026-07-08",
    "content": "Lite Mode\n\nLite mode is Spala's Copilot-first workspace. It keeps the AI Copilot, project graph, high-level resource counts, base API URL, frontend status, and common actions visible so you can understand and change a project without navigating every builder screen.\n\nSpala Lite mode showing the AI Copilot and project graph\nLite mode keeps the Copilot and project graph visible together.\n\nWhat Lite Mode Shows\n\nAI Copilot\nBuild or ask about the backend from the left panel.\n/features/ai-assistant\n\nProject Graph\nInspect tables, endpoints, functions, and relationships visually.\n/features/lite-mode\n\nResource Cards\nSee tables, endpoints, functions, tasks, agents, triggers, and publish state at a glance.\n/getting-started/interface\n\nBase API URL\nCopy the project API URL when connecting a frontend, integration, or coding agent.\n/features/api-docs\n\nFrontend Status\nCheck whether a hosted frontend exists and jump to frontend app controls.\n/features/frontend-app\n\nMode Switch\nMove between Lite and Advanced mode from the header.\n/getting-started/interface\n\nTools Menu\nOpen environment, docs, auth, MCP, settings, and publish tools from one compact menu.\n/settings\n\nWhat You Can Do From Lite Mode\n\n- Ask Copilot to create or change project resources\n- Review the project network before opening detailed editors\n- Copy the base API URL for frontend and integration work\n- See drafts, recent activity, and publish readiness\n- Jump into API docs, MCP, environment, auth, frontend app, snapshots, or settings\n\nWhen To Use Advanced Mode\n\nUse Advanced when you need the full visual builder: detailed table editing, endpoint configuration, function/code view, task and trigger editing, addons, pull requests, and low-level settings.\n\nSpala Advanced mode project overview with sidebar navigation\nAdvanced mode exposes the full builder navigation and resource editors.\n\nSwitch without losing context\n\n  The mode switch changes the product surface, not the project. Start with the Copilot workspace and switch to Advanced when you need direct control over a specific resource.\n\nAI Copilot\n/features/ai-assistant\nBuild and inspect a backend with AI\nThe Interface\n/getting-started/interface\nLearn the main Spala screens\nSettings\n/settings\nConfigure project, AI, MCP, docs, environment, and deployment settings"
  },
  {
    "path": "/features/project-agents",
    "title": "Project Agents",
    "description": "Build project-native agents that run manually, from chat, webhooks, schedules, database changes, or realtime events.",
    "section": "features",
    "url": "https://docs.spala.ai/features/project-agents/",
    "last_updated": "2026-07-08",
    "content": "Project Agents\n\nProject Agents are automations that live inside a Spala project. They are different from external AI coding agents connected through MCP: project agents are part of the backend you build and can run workflows for your app.\n\nSpala Project Agents screen with builder, chat, activity, timeline, settings, and metrics\nProject Agents include builder, chat, activity, timeline, settings, and metrics views.\n\nTrigger Types\n\nManual\nRun the agent yourself or start it from chat.\n/features/project-agents\n\nWebhook\nTrigger the agent from an HTTP request.\n/features/project-agents\n\nSchedule\nRun the agent on a repeat schedule.\n/features/project-agents\n\nDatabase Changes\nReact when project data is inserted, updated, or deleted.\n/features/project-agents\n\nWebSocket\nRespond to realtime messages and channel events.\n/features/project-agents\n\nWhat Agents Include\n\n- Builder - create or inspect the agent workflow.\n- Chat - run an agent conversationally with live input.\n- Activity - review recent executions, statuses, and output.\n- Timeline - inspect a step-by-step run timeline.\n- Settings - configure triggers, required environment variables, retry behavior, and execution limits.\n- Metrics - track active agents, execution volume, success rate, and errors.\n\nProject agents vs MCP agents\n\n  MCP agents are external tools like Codex, Claude, or Cursor that help build the backend. Project Agents are backend automations inside your Spala project.\n\nWhen To Use Project Agents\n\nUse project agents for recurring operational workflows, webhook handlers, internal assistants, data-change responders, and automations that need a visible execution history.\n\nAI Agents & MCP\n/agents\nConnect external coding agents to build with Spala\nScheduled Tasks\n/tasks/scheduled-tasks\nUse tasks for simpler scheduled functions\nTriggers\n/triggers\nRun logic directly from database events"
  },
  {
    "path": "/features/project-memory",
    "title": "Project Memory",
    "description": "Save durable project context so Copilot, repair flows, publish review, frontend generation, and MCP agents stay aligned.",
    "section": "features",
    "url": "https://docs.spala.ai/features/project-memory/",
    "last_updated": "2026-07-08",
    "content": "Project Memory\n\nProject Memory stores durable context for one generated project. Use it for decisions, constraints, preferences, warnings, and facts that AI should remember across future Copilot runs, repairs, publish reviews, frontend generation, and MCP agent work.\n\nWhat To Save\n\n- Business rules: \"Invoices must be immutable after payment.\"\n- Data constraints: \"Never expose password digest fields to AI data access.\"\n- Architecture decisions: \"Use Stripe Checkout, not Payment Intents, for hosted checkout.\"\n- Frontend expectations: \"The client portal is mobile-first and uses email login.\"\n- Deployment notes: \"Production deploys through the configured Render hook.\"\n\nHow It Works\n\nAdd memory\n\n    Open Settings > Project Memory and write the fact, decision, constraint, preference, or warning.\n\nSet scope\n\n    Choose a scope such as project, auth, frontend, deployment, data, agent, or resource.\n\nApprove or pin important items\n\n    Approved active memories can be used by AI. Pinned memories stay easy to find for critical context.\n\nArchive old context\n\n    Archive memory that is no longer true. Archived items remain available for audit but are ignored by AI.\n\nMemory should be true and useful\n\n  Do not store secrets in Project Memory. Put API keys and private values in Environment variables or addon settings.\n\nWhy Users Care\n\nAI gets better when it has stable product context. Project Memory prevents the same explanations from being repeated and helps different agents respect the same project rules.\n\nAI Copilot\n/features/ai-assistant\nUse memory as context for generation and Ask mode\nMCP\n/agents/mcp\nGive authenticated agents the right project context\nSettings\n/settings\nFind Project Memory in the settings modal"
  },
  {
    "path": "/features/project-overview",
    "title": "Project Overview",
    "description": "Use the project overview to inspect your backend, copy connection details, review drafts, and jump into the right editor.",
    "section": "features",
    "url": "https://docs.spala.ai/features/project-overview/",
    "last_updated": "2026-07-08",
    "content": "Project Overview\n\nThe Project Overview is the first place to check what Spala has built. It shows your backend resources, publish state, base API URL, frontend status, recent activity, and the project graph in one place.\n\nSpala Project Overview showing tables, endpoints, functions, tasks, and publish controls\nProject Overview gives you a scan-friendly view of the backend before you open detailed editors.\n\nWhat You Can See\n\nBase API URL\nCopy the URL your frontend, integrations, or agents should call.\n/features/api-docs\n\nResource Cards\nReview tables, endpoints, functions, and tasks with important fields and routes visible.\n/getting-started/interface\n\nEndpoint Summary\nSee the API routes that exist and jump into the full endpoint list when needed.\n/endpoints\n\nFrontend Status\nCheck whether a hosted frontend exists and open frontend app controls.\n/features/frontend-app\n\nDraft Changes\nSpot unpublished work before you publish the live API.\n/features/publish-export\n\nRecent Activity\nReview recent edits and operational events without leaving the overview.\n/features/project-overview\n\nProject Network\n\nThe Project Network tab shows how the project fits together: tables, endpoints, functions, tasks, agents, triggers, channels, and auth-related nodes. Use it to understand dependencies before changing or deleting resources.\n\nSpala Project Network graph showing connected backend resources\nThe project graph helps you find related resources and navigate from a high-level map.\n\nFrom the graph you can:\n\n- Inspect connected resources before making changes\n- Open a quick view for a node\n- Jump from the graph into the resource editor\n- Check whether an endpoint, trigger, task, or agent depends on another resource\n- Explain the project structure to a teammate or AI agent\n\nAgents Tab\n\nThe overview also gives quick access to project agents. Use this when you want to run or inspect an automation without leaving the project context.\n\nSpala Project Agents screen with builder, chat, activity, timeline, and settings tabs\nProject agents can be built, run from chat, reviewed in activity logs, and inspected through timeline and metrics views.\n\nOverview first, editor second\n\n  Start from Project Overview when you need to understand the whole backend. Open a focused editor when you need to change a table, endpoint, function, task, trigger, agent, or setting.\n\nLite Mode\n/features/lite-mode\nUse the Copilot workspace and project graph\nEditor Workspace\n/features/editor-workspace\nUse tabs, view modes, Ask AI, Try, and save controls\nPublish\n/features/publish-export\nReview and publish draft changes\nProject Agents\n/features/project-agents\nBuild backend automations inside your project"
  },
  {
    "path": "/features/project-snapshots",
    "title": "Project Snapshots",
    "description": "Create checkpoints, inspect diffs, and restore a full draft or selected resources after larger changes.",
    "section": "features",
    "url": "https://docs.spala.ai/features/project-snapshots/",
    "last_updated": "2026-07-08",
    "content": "Project Snapshots\n\nProject Snapshots are checkpoints for a Spala project draft. Use them before larger AI changes, refactors, imports, or deployments so you can inspect what changed and restore safely.\n\nWhat Snapshots Help With\n\n- Review changed tables, functions, endpoints, tasks, triggers, channels, and agents.\n- Restore the full draft state after a bad change.\n- Restore selected resources without undoing everything else.\n- Keep an audit trail of important project checkpoints.\n\nCreate And Restore\n\n Snapshots\n    Use this screen before a large Copilot run, agent edit, or manual refactor.\n\nCreate a snapshot\n\n    Add a short checkpoint message so future you knows why the snapshot exists.\n\nInspect the diff\n\n    Select a snapshot to see which resources changed.\n\nRestore carefully\n\n    Restore the whole draft or only selected resources, then review and publish when ready.\n\nSnapshot before broad AI edits\n\n  Before asking Copilot or an MCP agent to change many resources, create a snapshot. It gives you a clean recovery point if the result is not what you wanted.\n\nAI Copilot\n/features/ai-assistant\nCreate checkpoints before large AI changes\nPublish\n/features/publish-export\nReview and publish after restore or edit\nPull Requests\n/features/pull-requests\nRoute changes through review before merge"
  },
  {
    "path": "/features/publish-export",
    "title": "Publish",
    "description": "Review your generated backend changes and publish them as a live API from the Spala dashboard.",
    "section": "features",
    "url": "https://docs.spala.ai/features/publish-export/",
    "last_updated": "2026-07-08",
    "content": "Publish\n\nPublishing makes your current Spala project changes live. When you publish, Spala applies the project draft and updates the API your frontend or integrations call.\n\nThe Spala publish dialog\nReview pending changes before publishing your backend.\n\nPublishing Changes\n\nBuild or edit your backend\n\n    Use AI Copilot, Lite mode, or Advanced mode to create or change tables, functions, endpoints, tasks, triggers, channels, addons, and settings.\n\nReview the draft\n\n    Check the project graph, endpoint list, Playground, settings, and generated API docs before publishing.\n\nClick Publish\n\n    Click Publish in the top bar. Spala applies the changes and updates the live API.\n\nTest the live API\n\n    Use the Playground, generated endpoint snippets, or your frontend to confirm the API behaves as expected.\n\nNo platform deployment required\n\n  You publish your project from Spala. You do not deploy the Spala platform or manage its runtime infrastructure to use the hosted dashboard.\n\nDelivery Boundary\n\n| Action | What changes | Who manages it |\n| --- | --- | --- |\n| Publish backend API | Project draft becomes the live generated API | You review and approve publish from Spala |\n| Publish frontend app | Static frontend assets become the live project UI | You upload, preview, and publish from Frontend App |\n| Deploy generated project artifact | Generated project runtime is delivered to an external target | You configure and operate the external target |\n\nThe publish dialog showing changes ready to publish\nReview changes before publishing.\n\nTeam Review\n\nFor teams, Spala can route changes through Pull Requests before they go live:\n\n- Users with publish permission can publish directly.\n- Users without publish permission can prepare changes for review.\n- Reviewers can inspect changed tables, functions, endpoints, and settings before approving.\n\nVersion History\n\nSpala keeps project history so you can:\n\n- Review what changed and when\n- Compare versions of project resources\n- Recover from mistakes using snapshots and history tools\n- Understand who made a change before it was published\n\nAfter Publishing\n\nAfter publishing, use the API from:\n\n- Your frontend app\n- API Playground\n- Generated endpoint snippets\n- AI agents connected through the selected project MCP\n\nAPI Playground\n/features/playground\nTest your endpoints before and after publishing\nPull Requests\n/features/pull-requests\nReview and approve changes before publish\nProject Snapshots\n/features/project-snapshots\nCreate checkpoints before larger changes\nBuild a REST API\n/guides/build-rest-api\nCreate endpoints to publish\nSpala Public MCP\n/agents/mcp\nConnect AI agents through project handoff"
  },
  {
    "path": "/features/pull-requests",
    "title": "Pull Requests",
    "description": "Review backend changes before they merge into the project draft or become publishable.",
    "section": "features",
    "url": "https://docs.spala.ai/features/pull-requests/",
    "last_updated": "2026-07-08",
    "content": "Pull Requests\n\nPull Requests give teams a review path for Spala changes. Use them when a user or AI agent prepares backend changes that should be inspected before merging or publishing.\n\nSpala Pull Requests screen showing review status, changed resources, comments, and actions\nPull Requests give AI and human changes a review path before they merge into the project draft.\n\nWhat You Can Review\n\n- Changed database tables and fields\n- Updated functions and backend logic\n- Endpoint method, path, auth, and response changes\n- Scheduled or background task changes\n- Trigger, channel, and agent changes when included in a PR\n- Comments, approvals, and requested changes\n- AI review comments and AI-generated fix PRs\n- Draft, open, approved, merged, declined, and needs-rebase states\n\nReview Workflow\n\nOpen Pull Requests\n\n    Use the Pull Requests screen from Advanced mode when changes need team review.\n\nInspect the diff\n\n    Review changed models, functions, endpoints, tasks, and comments before merging.\n\nUse AI review when useful\n\n    Ask AI to review the PR for missing validation, broken assumptions, or obvious backend issues.\n\nApprove, request changes, or merge\n\n    Merge approved work into the project draft, then publish when the live API should change.\n\nClean up old review work\n\n    Close, reopen, delete, or bulk-delete PRs when they are no longer needed.\n\nUseful for agent work\n\n  Pull Requests are especially useful when an MCP agent or Copilot makes a broad change. They give humans a clear review point before anything becomes live.\n\nPublish\n/features/publish-export\nMake reviewed changes live\nProject Snapshots\n/features/project-snapshots\nKeep checkpoints around larger changes\nMCP\n/agents/mcp\nLet agents work through the project MCP"
  },
  {
    "path": "/features/team-access",
    "title": "Team Access",
    "description": "Manage builder users, roles, permissions, and secure access to the Spala project dashboard.",
    "section": "features",
    "url": "https://docs.spala.ai/features/team-access/",
    "last_updated": "2026-07-08",
    "content": "Team Access\n\nTeam Access controls who can use the Spala builder and what they can do. Use it when a project has multiple builders, reviewers, or operators.\n\nWhat You Manage\n\n- Builder users who can access the project dashboard\n- Roles and permissions for editing, publishing, settings, data, and review actions\n- Authentication and security settings for builder access\n- Trash and restore permissions for deleted resources\n\nWhy Users Care\n\nBackend builders need different levels of access. A developer may need to edit functions, a reviewer may only approve changes, and an operator may only inspect logs or data. Roles keep those responsibilities separate.\n\nGive publish permission deliberately\n\n  Publishing changes the live API. Keep publish access limited to users who should be able to affect production behavior.\n\nRelated Settings\n\n- Users & Roles - invite or manage dashboard users and permissions.\n- Security - configure origins, trusted hosts, tokens, and request controls.\n- Trash Bin - restore deleted resources or permanently remove them.\n\nSettings\n/settings\nOpen Users & Roles and Security settings\nPull Requests\n/features/pull-requests\nReview changes before merge or publish\nPublish\n/features/publish-export\nUnderstand what publish permission controls"
  },
  {
    "path": "/features/templates",
    "title": "Use AI Copilot",
    "description": "Build from a plain-language prompt instead of choosing a starter pattern.",
    "section": "features",
    "url": "https://docs.spala.ai/features/templates/",
    "last_updated": "2026-07-08",
    "content": "Use AI Copilot\n\nThe recommended way to start a Spala project is the AI Copilot. Describe the backend you need in plain language, review the generated tables, functions, endpoints, and settings, then publish when it looks right.\n\nStart with a prompt\n\n  You do not need to choose a starter pattern first. Tell Spala what the app should do, then refine the generated backend with Copilot, Lite mode, or Advanced mode.\n\nAI Copilot\n/features/ai-assistant\nDescribe what you want and let Spala build it\nLite Mode\n/features/lite-mode\nUse the Copilot workspace and project graph\nQuick Start\n/getting-started/quickstart\nBuild a simple API from the dashboard"
  },
  {
    "path": "/flows/code-view",
    "title": "Code View",
    "description": "Use JavaScript alongside Spala's Gherkin, Visual, and Split views.",
    "section": "flows",
    "url": "https://docs.spala.ai/flows/code-view/",
    "last_updated": "2026-07-08",
    "content": "Code View\n\nUse JavaScript alongside Spala's Gherkin, Visual, and Split views. Built-in steps round-trip cleanly between visual views and code; custom JavaScript is preserved as Custom Code when it cannot be represented as a built-in step.\n\nThe code view showing JavaScript for a function\nEvery function has a Code tab next to Gherkin, Visual, and Split.\n\nHow to switch\n\n1. Open any function in the editor\n2. Click the Code tab at the top\n3. Edit the JavaScript directly\n4. Switch back to Gherkin, Visual, or Split to review the function visually\n\nPro tip\n\n  Built-in steps stay connected across visual views and code. Unsupported JavaScript is preserved as Custom Code so you do not lose work.\n\nWhat it looks like\n\nHere is how common steps translate to JavaScript:\n\nSet Variable:\n\nlet greeting = \"Hello, \" + input.name;\n\nDatabase Query:\n\nlet cases = await db.query(\"SELECT * FROM cases WHERE client_id = $1\", [vars.user.id]);\n\nIf/Else:\n\nif (vars.cases.length === 0) {\n  return { message: \"No cases yet\" };\n}\n\nLoop:\n\nfor (let caseRecord of vars.cases) {\n  // steps inside the loop\n}\n\nWhen to use code view\n\n- Complex expressions — Multi-line logic that is awkward in the properties panel\n- Rapid prototyping — Writing code can be faster than dragging steps\n- Copy-paste — Duplicate logic by copying code blocks\n- Refactoring — Restructure function logic by editing code directly\n\nWhen to use visual view\n\n- Getting an overview — The visual editor shows function structure at a glance\n- Configuring steps — The properties panel surfaces all options for each step type\n- Collaboration — Visual functions are easier for non-coders to read and modify\n\nGood to know\n\n  If you write JavaScript that does not map to a built-in step type (like a class definition), it gets wrapped in a Custom Code step. The code is preserved, but appears as a single block in the visual editor.\n\nCreate a Function\n/flows/creating-flows\nBuild functions in the visual editor\nVariables & Data\n/flows/variables\nWork with data in both views\nTry & Runs\n/flows/debugging\nTest your functions"
  },
  {
    "path": "/flows/creating-flows",
    "title": "Create a Function",
    "description": "Step-by-step guide to building backend logic in Spala.",
    "section": "flows",
    "url": "https://docs.spala.ai/flows/creating-flows/",
    "last_updated": "2026-07-08",
    "content": "Create a Function\n\nFunctions contain your backend logic. Each function is a reusable sequence of steps that you can attach to endpoints, tasks, triggers, agents, or other functions.\n\nBuild your first function\n\nGo to Functions\n\n    Click Functions in the left sidebar.\n\nAdd a new function\n\n    Click Create Function and give it a descriptive name, like \"List Cases\" or \"Create Order\".\n\nChoose the right view\n\n    Start in Gherkin or Visual view to understand the function outline, then switch to Split or Code when you want more detail.\n\nConfigure each step\n\n    In Visual view, select a step and fill in its settings. Use Add Step search to find database, API, auth, control-flow, and addon steps.\n\nSave\n\n    Press Ctrl+S (or Cmd+S) to save. Your function is now ready to attach to an endpoint, task, trigger, or agent.\n\nThe function editor showing inputs, output, and execution outline\nStart from the readable outline to review inputs, output, and execution path.\n\nThe editor layout\n\n- Gherkin — A readable behavior outline for reviewing what the function does.\n- Visual — Editable step blocks and inputs.\n- Split — Visual and Code together.\n- Code — JavaScript for advanced editing.\n\nCommon steps to start with\n\n| Step           | What to use it for                                  |\n|---------------|-----------------------------------------------------|\n| Set Variable  | Store a value: greeting = \"Hello, \" + input.name |\n| Database Query| Read or write data: select, insert, update, delete  |\n| If/Else       | Add a condition: if vars.user exists, else 404    |\n| Return        | Send the response: return vars.cases               |\n\nGood to know\n\n  A single function can be used by multiple endpoints. You can also call one function from another using a Run Function step.\n\nTips\n\n- Duplicate a function — Right-click a function and choose Duplicate to create a copy you can modify.\n- Name functions descriptively — Use names like \"List Cases\", \"Create Order\", \"Send Welcome Email\" so you can find them quickly.\n- Keep functions focused — One function per action. If a function gets too long, split it into smaller functions and use Run Function to call them.\n\nVariables & Data\n/flows/variables\nSet and use variables in your functions\nCode View\n/flows/code-view\nSee and edit the JavaScript version\nTry & Runs\n/flows/debugging\nTest your functions with sample data"
  },
  {
    "path": "/flows/debugging",
    "title": "Try & Runs",
    "description": "Test functions with sample data, inspect variables, and review execution history.",
    "section": "flows",
    "url": "https://docs.spala.ai/flows/debugging/",
    "last_updated": "2026-07-08",
    "content": "Try & Runs\n\nUse Try to test a function with sample data, inspect variables, and review each execution. This helps you catch missing inputs, broken conditions, and unexpected API responses before publishing.\n\nTry a function\n\nOpen the function\n\n    Go to Functions and open the function you want to test.\n\nClick Try\n\n    Click the Try button in the toolbar.\n\nSet test inputs\n\n    Fill in sample data for the request body, path parameters, or query string - whatever your function expects.\n\nRun and inspect\n\n    Run the function and inspect each step result. The run view shows outputs, variables, and errors.\n\nSet breakpoints\n\nBreakpoints pause execution at a specific step so you can inspect the variable state.\n\n1. Hover over a step in the editor\n2. Click the dot that appears on the left edge\n3. A red dot confirms the breakpoint\n4. Try the function - it pauses at the breakpoint\n\nWhen paused, you can:\n\n| Control        | What it does                                     |\n|---------------|--------------------------------------------------|\n| Continue  | Run until the next breakpoint or the end         |\n| Step Over | Execute just this step, then pause again          |\n| Stop      | Cancel the function run                           |\n\nInspect variables\n\nThe variable inspector shows every variable and its current value after each step. You can see:\n\n- vars — Variables you set during the function run\n- input — The request body\n- params — Path parameters\n- query — Query string values\n\nObjects and arrays can be expanded to drill into their contents.\n\nPro tip\n\n  Use step-by-step execution (click Step Over repeatedly) to walk through a function without setting breakpoints. The inspector updates after each step.\n\nTroubleshooting tips\n\nVariable is undefined?\nCheck that the step assigning it actually runs. It might be inside an If/Else branch that was not taken.\n\nDatabase query returns empty?\nUse the variable inspector to check your WHERE condition values. Then verify the data exists using the Data tab in the Database section.\n\nAPI call fails?\nInspect the URL, headers, and body in the variable inspector. Check that env variables like API keys are set correctly.\n\nExecution history\n\nOpen Runs from the resource menu to review previous executions. Endpoint, task, trigger, and function runs can include:\n\n- Duration, status (success/error), and timestamp\n- The full request and response data\n- Step-by-step execution logs\n- Analyze with AI — click to send an error directly to the AI Copilot for diagnosis\n\nCreate a Function\n/flows/creating-flows\nBuild your function\nVariables & Data\n/flows/variables\nUnderstand the data available to your function\nCode View\n/flows/code-view\nDebug using the JavaScript view"
  },
  {
    "path": "/flows/variables",
    "title": "Variables & Data",
    "description": "Set variables, access request data, and use expressions in your functions.",
    "section": "flows",
    "url": "https://docs.spala.ai/flows/variables/",
    "last_updated": "2026-07-08",
    "content": "Variables & Data\n\nVariables store data as your function runs. You create them with Set Variable steps and reference them in any step that follows.\n\nThe step properties panel showing variable configuration\nConfigure step properties and variables in the right panel\n\nSet a variable\n\nUse a Set Variable step to create or update a variable:\n\n- Variable Name — e.g., greeting, totalPrice, isAdmin\n- Value — An expression that computes the value\n\n| Variable Name | Value                              |\n|--------------|-------------------------------------|\n| greeting   | \"Hello, \" + input.name          |\n| totalPrice | input.price * input.quantity     |\n| isAdmin    | vars.user.role === \"admin\"       |\n\nBuilt-in variables\n\nEvery function has access to these built-in data sources:\n\n| Variable  | What it contains                              | Example                    |\n|-----------|----------------------------------------------|----------------------------|\n| vars    | Variables you set during the function        | vars.cases               |\n| input   | The request body (POST, PUT, PATCH)          | input.name               |\n| params  | URL path parameters                          | params.id                |\n| query   | URL query string parameters                  | query.page               |\n| headers | HTTP request headers                         | headers.authorization    |\n| env     | Server environment variables                 | env.JWT_SECRET           |\n\nExamples\n\n// Path: GET /api/cases/:id?include=updates\nparams.id          // \"42\"\nquery.include      // \"updates\"\n\n// Body: POST /api/cases  { \"case_name\": \"Contract Review\" }\ninput.case_name    // \"Contract Review\"\n\n// Environment\nenv.API_KEY        // Your configured API key\n\nGood to know\n\n  Query string and path parameter values are always strings. Use params.id | toNumber or Number(params.id) when you need a number.\n\nUsing expressions\n\nExpressions let you compute values dynamically. They use JavaScript syntax and work in any step property:\n\n// Arithmetic\ninput.price * input.quantity\n\n// String concatenation\n\"Hello, \" + input.name\n\n// Ternary\nvars.count > 0 ? \"found\" : \"empty\"\n\n// Access nested data\nvars.user.address.city\nvars.products[0].name\n\n// Default values\ninput.page || 1\nquery.limit ? Number(query.limit) : 20\n\nFilters\n\nFilters transform values using a pipe (|) syntax. They are a shorthand for common operations:\n\ninput.email | lowercase | trim\nquery.page | toNumber | default: 1\nvars.items | map: \"name\"\nvars.name | uppercase\n\nPro tip\n\n  Check the Filters Reference for the full list of available filters.\n\nVariable scoping\n\n- Variables are scoped to the current function execution\n- A variable set inside an If/Else branch is still accessible after the branch\n- Each request creates a fresh context — variables do not leak between requests\n\nCreate a Function\n/flows/creating-flows\nBuild your first function step by step\nCode View\n/flows/code-view\nSee variables as JavaScript code\nTry & Runs\n/flows/debugging\nInspect variable values at each step"
  },
  {
    "path": "/addons",
    "title": "Addons",
    "description": "Extend your backend with ready-to-use integrations for payments, email, AI, storage, and more.",
    "section": "general",
    "url": "https://docs.spala.ai/addons/",
    "last_updated": "2026-07-08",
    "content": "Addons\n\nExtend your backend with ready-to-use integrations for payments, email, AI, storage, and more.\n\nAddons marketplace showing available integrations\nBrowse and install addons from the marketplace.\n\nHow addons work\n\nEnable an addon and new steps appear in your flow editor. Configure them with your API keys and common requests are preconfigured, so you spend less time wiring provider APIs by hand.\n\nFor example, enabling the Stripe addon gives you steps like \"Create Payment Intent\" and \"Create Customer.\" Enabling OpenAI gives you AI steps for text and image workflows. Each step handles authentication, request formatting, and error handling for you.\n\nPopular addons\n\nStripe\nAccept payments, manage subscriptions, and handle refunds.\n/addons/catalog/stripe\n\nOpenAI\nAdd AI chat, text generation, and image creation to your API.\n/addons/catalog/openai\n\nSendGrid\nSend transactional and marketing emails at scale.\n/addons/catalog/sendgrid\n\nAWS S3\nStore and serve files in the cloud.\n/addons/catalog/aws-s3\n\nTwilio\nSend SMS messages.\n/addons/catalog/twilio-sms\n\nSlack\nSend notifications and messages to Slack channels.\n/addons/catalog/slack-api\n\nHow to enable an addon\n\nOpen the Addons screen\n\n    Go to Addons from the sidebar. Browse the marketplace or search for the addon you need.\n\nClick Enable\n\n    Click the addon card and hit Enable. The addon's steps are now available in your flow editor.\n\nAdd your API key\n\n    Go to Settings > Environment Variables and add the API key the addon needs (e.g., STRIPE_SECRET_KEY). Each addon's page tells you which variables to set.\n\nAll addons\n\nAnthropic\nClaude AI models for message generation.\n/addons/catalog/anthropic\n\nAWS S3\nStore and serve files in the cloud.\n/addons/catalog/aws-s3\n\nDiscord\nSend messages and notifications to Discord channels.\n/addons/catalog/discord\n\nFirebase Auth\nVerify Firebase ID tokens and manage users.\n/addons/catalog/firebase-auth\n\nGitHub\nInteract with repositories, issues, and pull requests.\n/addons/catalog/github\n\nHTML to PDF\nGenerate PDF documents from HTML templates.\n/addons/catalog/html-to-pdf\n\nHubSpot\nManage contacts, deals, and CRM data.\n/addons/catalog/hubspot\n\nImage Processing\nResize, crop, convert, and transform images.\n/addons/catalog/image-processing\n\nJira\nCreate and manage Jira issues and projects.\n/addons/catalog/jira\n\nLocal Storage\nRead and write project files in managed storage.\n/addons/catalog/local-storage\n\nNotion\nCreate pages, query databases, and manage content.\n/addons/catalog/notion\n\nOpenAI\nAI chat, text generation, and image creation.\n/addons/catalog/openai\n\nQR Code\nGenerate QR codes as images or data URIs.\n/addons/catalog/qr-code\n\nRedis Cloud\nCaching, session storage, and pub/sub messaging.\n/addons/catalog/redis-cloud\n\nSendGrid\nSend transactional and marketing emails at scale.\n/addons/catalog/sendgrid\n\nShopify\nManage products, orders, and customers.\n/addons/catalog/shopify\n\nSlack\nSend notifications and messages to Slack channels.\n/addons/catalog/slack-api\n\nStripe\nAccept payments, manage subscriptions, and refunds.\n/addons/catalog/stripe\n\nSupabase\nDatabase queries, authentication, and storage.\n/addons/catalog/supabase\n\nTwilio SMS\nSend SMS messages.\n/addons/catalog/twilio-sms\n\nNeed a custom integration?\n\n  Start with the built-in External API Call step, webhooks, or AI Copilot. If you need a reusable integration that is not in the catalog, contact Spala support.\n\nInstalling Addons\n/addons/installing\nEnable and configure addons in your project\nExternal API Steps\n/reference/steps/api\nCall services that are not in the addon catalog\nAI Copilot\n/features/ai-assistant\nAsk Spala to wire an integration into your backend"
  },
  {
    "path": "/agents",
    "title": "AI Agents & MCP",
    "description": "Connect AI coding agents to Spala through the public MCP, authenticate with Spala, select a project, and hand off to the project MCP.",
    "section": "general",
    "url": "https://docs.spala.ai/agents/",
    "last_updated": "2026-07-08",
    "content": "AI Agents & MCP\n\nSpala exposes a public MCP endpoint for AI coding agents that need to discover Spala, authenticate with the Spala platform, select a project, and hand off to the selected project MCP.\n\nThe public endpoint is:\n\nhttps://mcp.spala.ai/mcp\n\nSpala also publishes machine-readable MCP discovery at:\n\nhttps://spala.ai/.well-known/mcp.json\n\nDo not guess project MCP URLs\n\n  Project MCP URLs are resolved after Spala platform authentication from project/access-url data. Do not hardcode patterns like https://api.spala.ai/{project}/mcp.\n\nPublic MCP vs project MCP\n\n| MCP surface | Purpose |\n| --- | --- |\n| https://mcp.spala.ai/mcp | Public discovery, OAuth metadata, agent onboarding, authenticated project lookup, project handoff |\n| Project MCP | Backend work for one project: inspect context, make changes, validate, publish, and review |\n| OAuth authorization server | Discovered from the MCP OAuth metadata. Do not configure it manually as the MCP server URL. |\n\nProject creation status\n\n  project_create is dry-run only in the current public MCP deployment. It previews project creation but does not create a real project.\n\nStart here\n\nPublic MCP\nUnderstand the discovery, authentication, project selection, and handoff flow.\n/agents/mcp\n\nCodex Setup\nAdd Spala Public MCP to Codex and start with onboarding/tool-map calls.\n/agents/codex\n\nClaude Setup\nAdd Spala Public MCP to Claude Code over HTTP transport.\n/agents/claude\n\nCursor Setup\nUse the same public MCP URL and let Spala resolve project MCP after auth.\n/agents/cursor\n\nGemini CLI Setup\nConfigure Gemini with Spala Public MCP and the project handoff workflow.\n/agents/gemini\n\nVS Code and Copilot Setup\nUse Spala Public MCP from VS Code or GitHub Copilot MCP support.\n/agents/vscode\n\nWindsurf Setup\nConfigure Windsurf with Spala Public MCP and safe project handoff.\n/agents/windsurf\n\nCline Setup\nConfigure Cline with Spala Public MCP and authenticated project selection.\n/agents/cline\n\nPublic Agent Skills\nUse public Spala skill files before authenticated project work.\n/agents/skills\n\nPublic MCP\n/agents/mcp\nDiscovery, auth, project selection, and project MCP handoff\nPublic Agent Skills\n/agents/skills\nAgent-readable skill files for evaluation and safe workflow\nQuick Start\n/getting-started/quickstart\nBuild a simple REST API from the dashboard\nPublish\n/features/publish-export\nValidate and publish your backend API"
  },
  {
    "path": "/endpoints",
    "title": "API Endpoints",
    "description": "Create REST API endpoints and connect them to your backend logic.",
    "section": "general",
    "url": "https://docs.spala.ai/endpoints/",
    "last_updated": "2026-07-08",
    "content": "API Endpoints\n\nCreate REST API endpoints that your frontend or any HTTP client can call. Each endpoint maps a URL and HTTP method to a function that handles the request.\n\nThe API section showing all endpoints organized by route prefix in the sidebar\nAll your endpoints organized by route prefix — click any to edit\n\nQuick walkthrough\n\nCreate an endpoint\n\n    Click Endpoints in the sidebar, then Add Endpoint.\n\nSet method and path\n\n    Choose the HTTP method (GET, POST, PUT, DELETE) and enter a path like /api/cases or /api/cases/:id. The :id segment is a path parameter - it matches any value and is available in your function as params.id.\n\nAttach a function\n\n    Select the function that handles the request. The function runs when someone calls this endpoint.\n\nTest it\n\n    Open the Playground and send a request to see the response.\n\nPublish when ready\n\n    Review the changes and publish when the endpoint should be available to live clients.\n\nPro tip\n\n  Most APIs follow a standard pattern. For a cases resource, you would typically create five endpoints: list, get, create, update, and delete.\n\nCommon endpoint patterns\n\n| Method | Path            | Function       | What it does       |\n|--------|----------------|----------------|--------------------|\n| GET    | /api/cases       | List Cases     | Get all cases      |\n| GET    | /api/cases/:id   | Get Case       | Get one case       |\n| POST   | /api/cases       | Create Case    | Create a new case  |\n| PUT    | /api/cases/:id   | Update Case    | Update a case      |\n| DELETE | /api/cases/:id   | Delete Case    | Delete a case      |\n\nLearn more\n\nEndpoint Controls\n\nEndpoints can also control operational behavior:\n\n- Authentication - require JWT auth for protected routes.\n- Inputs - define path, query, body, and file inputs for clearer testing and docs.\n- Rate limiting - protect expensive or public routes from abuse.\n- Hide from API docs - keep internal or experimental routes out of generated public docs.\n- AI summary - ask AI to draft a human-readable endpoint description for docs and review.\n- Requests - inspect recent request history when debugging live behavior.\n\nCreate an Endpoint\nStep-by-step guide to setting up endpoints with methods, paths, and functions.\n/endpoints/creating-endpoints\n\nInputs & Outputs\nHandle path parameters, query strings, request bodies, and responses.\n/endpoints/inputs-outputs\n\nProtect Your Endpoints\nAdd authentication with JWT tokens and role-based access.\n/endpoints/authentication\n\nFunctions\n/flows\nBuild the logic that powers your endpoints\nPublish\n/features/publish-export\nReview and publish endpoints for live clients\nBuild a REST API\n/guides/build-rest-api\nStep-by-step guide to creating a complete API"
  },
  {
    "path": "/",
    "title": "Build backends for AI-built apps",
    "description": "Spala turns product intent into database, auth, REST APIs, backend logic, docs, publishing, and MCP access for coding agents.",
    "section": "general",
    "url": "https://docs.spala.ai/",
    "last_updated": "2026-07-08",
    "content": "Build backends for AI-built apps\n\nTell Spala what your app needs and get a backend you can inspect, edit, validate, and publish: database, auth, REST APIs, backend logic, docs, and agent access.\n\nThe Spala Lite workspace showing AI Copilot and the project graph\nStart from the Copilot workspace and inspect the generated project graph.\n\nThree ways to build\n\nAI Copilot\nDescribe what you want in plain English. The AI builds your tables, flows, and endpoints — ready to use in seconds.\n/features/ai-assistant\n\nLite Mode\nStart with a Copilot workspace and project graph, then jump into focused backend tools.\n/features/lite-mode\n\nFunction Editor\nUse Gherkin, Visual, Split, and Code views to inspect and refine API behavior. No boilerplate, no setup.\n/flows\n\nCode View\nUse Code view for advanced JavaScript while keeping built-in steps connected to visual views.\n/flows/code-view\n\nAI Agents & MCP\nConnect Codex, Claude, Cursor, or another MCP client to discover Spala, authenticate, select a project, and hand off to the project MCP.\n/agents/mcp\n\nProject Agents\nCreate backend automations that run from chat, webhooks, schedules, database changes, or realtime events.\n/features/project-agents\n\nFrontend App\nPreview and publish a static app UI next to the generated backend, or let an agent prepare the upload.\n/features/frontend-app\n\nStart building in 5 minutes\n\n  Choose the right first path on Start Here, then follow the Quick Start to build a Cases API. If you are using an AI coding assistant, start with Spala Public MCP.\n\nExplore the docs\n\nStart Here\nPick the dashboard, frontend, AI agent, or raw MCP path.\n/start-here\n\nQuick Start\nBuild a Cases API in under 5 minutes.\n/getting-started/quickstart\n\nAddons\nIntegrations for payments, email, AI, storage, and more.\n/addons\n\nGuides\nStep-by-step tutorials for building real features.\n/guides\n\nMCP Setup\nInstall the public Spala MCP and let authenticated project data resolve the project MCP URL.\n/agents/mcp\n\nSettings\nConfigure AI, MCP, API docs, environment variables, snapshots, auth, and project controls.\n/settings\n\nAPI Docs\nShare OpenAPI, Markdown docs, SDK files, and snippets with frontend builders or agents.\n/features/api-docs\n\nProject Memory\nSave durable project context that Copilot, publish review, frontend generation, and MCP agents can use.\n/features/project-memory\n\nQuick Start\n/getting-started/quickstart\nBuild a Cases API in under 5 minutes\nSpala Public MCP\n/agents/mcp\nConnect an AI coding agent to Spala safely\nAI Copilot\n/features/ai-assistant\nDescribe what you want and the AI builds it for you\nLite Mode\n/features/lite-mode\nUse the Copilot workspace and project graph\nPublish\n/features/publish-export\nReview and publish your backend API"
  },
  {
    "path": "/channels",
    "title": "Channels",
    "description": "Manage realtime channels for database changes, broadcasts, and presence when Channels are enabled for your project.",
    "section": "general",
    "url": "https://docs.spala.ai/channels/",
    "last_updated": "2026-07-08",
    "content": "Channels\n\nChannels let a project push live updates to clients when realtime capabilities are enabled. Use them for database-change feeds, broadcasts, presence, live dashboards, chat, and collaborative interfaces.\n\nAdvanced availability\n\n  Channels are not the main starting point for most projects. If you do not see Channels in your dashboard, build with API Endpoints, Triggers, and Scheduled Tasks, or contact Spala support to confirm whether Channels are enabled for your project.\n\nThe Channels section showing realtime channel list and editor\nManage realtime channels from the Channels screen when enabled for the project.\n\nChannel types\n\nThe current Channels workspace focuses on realtime channel behavior:\n\n- Database Changes - push events when selected project data changes.\n- Broadcast - send messages to connected clients subscribed to a channel.\n- Presence - track connected users or sessions for collaborative and live-status features.\n\nAdvanced REST-channel organization may be available in some workspaces, but most projects should start with regular API Endpoints and add realtime channels only when the product needs live updates.\n\nLearn more\n\nREST Channels\nGroup and configure REST API endpoints with shared settings.\n/channels/rest-channels\n\nRealtime Channels\nSet up WebSocket connections for live data updates.\n/channels/realtime\n\nBuild Realtime Chat\n/guides/realtime-chat\nFull tutorial for building a chat backend\nTriggers\n/triggers\nAuto-run logic that can emit realtime events"
  },
  {
    "path": "/database",
    "title": "Database",
    "description": "Design your database visually — create tables, add fields, and set up relationships.",
    "section": "general",
    "url": "https://docs.spala.ai/database/",
    "last_updated": "2026-07-08",
    "content": "Database\n\nDesign your database visually — create tables, add fields, set up relationships. No SQL required.\n\nThe database tables list showing all tables in the project\nAll your tables at a glance\n\nQuick walkthrough\n\nCreate a table\n\n    Click Database in the sidebar, then Add Table. Give it a name like cases. An id column is added automatically.\n\nAdd fields\n\n    Click Add Field and choose a type (Text, Number, Boolean, Date, etc.). Set whether the field is required, unique, or has a default value.\n\nSave\n\n    Click Save and your table is created. Spala handles the database setup for you.\n\nGood to know\n\n  Spala uses PostgreSQL under the hood. You never need to write SQL, but you can use Raw SQL steps in your functions if you want to.\n\nUse your tables in functions\n\nOnce your tables exist, you interact with them through Database Query steps in your functions:\n\n- Select — Read rows with conditions, sorting, and pagination\n- Insert — Create new rows\n- Update — Modify existing rows\n- Delete — Remove rows\n\nSchema Visualizer\n\nClick the Visualize button (the link icon next to DATABASE in the sidebar header) to open an interactive ER diagram of your entire database.\n\nThe Schema Visualizer showing an ER diagram with all tables, fields, and relationship lines\nSee all your tables and relationships in one diagram\n\nThe visualizer shows:\n\n- Every table with its fields, types, and constraints\n- Primary keys (🔑) and foreign keys (→) highlighted\n- Relationship lines connecting tables through foreign key references\n- Reset Layout to auto-arrange the diagram\n\nClick any table's edit icon to jump directly to its schema editor. Drag tables to rearrange the layout.\n\nLearn more\n\nTables & Fields\nCreate tables, add fields, and manage your schema.\n/database/tables\n\nRelationships\nConnect tables with foreign keys for one-to-many and many-to-many patterns.\n/database/relationships\n\nDatabase Steps\n/reference/steps/database\nReference for all database step types in functions\nDatabase Triggers\n/triggers/database-triggers\nAuto-run logic when rows are inserted, updated, or deleted\nPublish\n/features/publish-export\nMake your project API live after schema changes"
  },
  {
    "path": "/flows",
    "title": "Functions",
    "description": "Build backend logic with Gherkin, Visual, Split, and Code views.",
    "section": "general",
    "url": "https://docs.spala.ai/flows/",
    "last_updated": "2026-07-08",
    "content": "Functions\n\nFunctions contain your backend logic. Build them with Gherkin, Visual, Split, and Code views. Query your database, send emails, call external APIs, and switch to JavaScript when you need more control.\n\nThe Spala function editor showing a readable function outline\nThe readable outline summarizes the function contract and execution path.\n\nWhat is a function?\n\nA function is a sequence of steps that executes your backend logic. Functions run when an endpoint is called, a task fires, a database trigger activates, an agent runs, or another function calls them. Steps execute top to bottom. Each step does one thing: query data, set a variable, check a condition, call an API, or return a response.\n\nQuick example\n\nHere is a function that gets a case by ID with a 404 fallback:\n\n1. Database Query — Select from cases where id = params.id, assign to caseRecord\n2. If/Else — Condition: vars.caseRecord\n   - True: Return vars.caseRecord\n   - False: Return { error: \"Not found\" } with status 404\n\nAttach this to a GET /api/cases/:id endpoint and you have a working API route.\n\nPro tip\n\n  Built-in steps stay connected across visual views and Code view. Custom JavaScript is preserved when it cannot be represented as a built-in step.\n\nCommon step types\n\n| Step              | What it does                                    |\n|------------------|-------------------------------------------------|\n| Set Variable     | Store a value for later steps                   |\n| Database Query   | Read, insert, update, or delete database rows   |\n| If/Else          | Branch logic based on a condition               |\n| Loop             | Repeat steps for each item in a list            |\n| Return           | Send a response back to the client              |\n| External API Call| Call an outside API (Stripe, OpenAI, etc.)      |\n| Try/Catch        | Handle errors gracefully                        |\n| Run Function     | Call another function as a subroutine           |\n\nLearn more\n\nCreate a Function\nStep-by-step guide to building your first function.\n/flows/creating-flows\n\nVariables & Data\nSet variables and access request data, environment variables, and more.\n/flows/variables\n\nCode View\nUse JavaScript alongside visual flow views.\n/flows/code-view\n\nTry & Runs\nUse Try, runs, breakpoints, and variable inspection to test functions.\n/flows/debugging\n\nSteps Reference\n/reference/steps\nComplete reference for available step types\nFilters Reference\n/reference/filters\nData transformation filters for expressions\nAPI Endpoints\n/endpoints\nConnect your functions to REST API endpoints"
  },
  {
    "path": "/functions",
    "title": "Functions",
    "description": "Spala functions are backend workflows. The current docs route is /flows because older builds used the word flow for the same editor surface.",
    "section": "general",
    "url": "https://docs.spala.ai/functions/",
    "last_updated": "2026-07-08",
    "content": "Functions\n\nSpala functions are backend workflows that power endpoints, tasks, triggers, project agents, and internal automation. Some older docs and URLs use the word flows for the same editor surface.\n\nTerminology\n\n  In the current product UI, think Functions. In docs URLs, /flows is the canonical route for the function editor and related reference pages.\n\nUse functions when you need to:\n\n- Read and write database records\n- Validate inputs\n- Call APIs and addons\n- Send emails, files, payments, or AI requests\n- Return API responses\n- Run logic from endpoints, tasks, triggers, or project agents\n\nFunctions Overview\n/flows\nBuild backend logic with the function editor\nCreate a Function\n/flows/creating-flows\nCreate and connect function steps\nCode View\n/flows/code-view\nEdit function logic as JavaScript\nVariables & Data\n/flows/variables\nPass data between function steps"
  },
  {
    "path": "/guides",
    "title": "Guides",
    "description": "Step-by-step tutorials for building real features with Spala.",
    "section": "general",
    "url": "https://docs.spala.ai/guides/",
    "last_updated": "2026-07-08",
    "content": "Guides\n\nStep-by-step tutorials for building real features. Each guide walks you through a complete, working example you can adapt to your own project.\n\nBuild a REST API\n/guides/build-rest-api\n\n    Create a cases API with tables, endpoints, and CRUD operations in minutes.\n\nAdd User Authentication\n/guides/user-auth\n\n    Set up signup, login, JWT tokens, and protected endpoints.\n\nAccept Payments with Stripe\n/guides/stripe-payments\n\n    Install the Stripe addon, create a checkout function, and handle webhooks.\n\nSend Emails\n/guides/send-emails\n\n    Configure email delivery and send transactional emails from your functions.\n\nUpload Files\n/guides/file-uploads\n\n    Accept file uploads, store them in S3 or locally, and serve them back.\n\nAdd AI Features\n/guides/ai-integration\n\n    Build a chat completion endpoint with the OpenAI addon.\n\nBuild Realtime Chat\n/guides/realtime-chat\n\n    Set up WebSocket channels and broadcast messages to connected clients.\n\nAdvanced CRUD\n/guides/crud-operations\n\n    Add pagination, filtering, sorting, and batch operations to your endpoints.\n\nRun Scheduled Jobs\n/guides/scheduled-jobs\n\n    Automate cleanup routines, reports, and data syncing with tasks.\n\nQuick Start\n/getting-started/quickstart\nBuild your first API in under 5 minutes\nFunctions\n/flows\nLearn the fundamentals of building backend logic\nAddons\n/addons\nExtend your backend with integrations"
  },
  {
    "path": "/self-hosting",
    "title": "Legacy Self-Hosting Links",
    "description": "Legacy compatibility page for old self-hosting URLs. Spala is used from the hosted dashboard.",
    "section": "general",
    "url": "https://docs.spala.ai/self-hosting/",
    "last_updated": "2026-07-08",
    "content": "Legacy Self-Hosting Links\n\nSpala is designed to be used from the hosted dashboard at https://dashboard.spala.ai. You create a project, describe what you need in the AI Copilot, inspect the generated backend, publish the API, and connect your frontend or AI agent.\n\nThis page is kept only for old self-hosting links. The current docs for getting projects live are in Project Delivery. Users do not self-host the Spala dashboard, builder, or platform source code.\n\nStart in the dashboard\n\n  If you want to build a backend, open the hosted dashboard and start with AI Copilot or Lite mode. Spala handles the platform behind the scenes.\n\nWhat You Manage\n\n- Your project resources: tables, endpoints, functions, tasks, triggers, channels, addons, and settings\n- Your published API behavior\n- Environment variables for your project integrations\n- Agent access through Spala MCP\n- Project publishing and review\n\nWhat Spala Manages\n\n- Platform hosting and dashboard delivery\n- Builder runtime\n- Managed project infrastructure\n- Platform updates and operational maintenance\n\nProject Delivery\n/deployment\nPublish and connect the projects you build in Spala\nAI Copilot\n/features/ai-assistant\nDescribe what you need and let Spala build the backend\nLite Mode\n/features/lite-mode\nUse the Copilot workspace and project graph\nPublish Your API\n/features/publish-export\nMake your project changes live\nSpala Public MCP\n/agents/mcp\nConnect AI agents through the public MCP handoff"
  },
  {
    "path": "/deployment",
    "title": "Project Delivery",
    "description": "Publish and connect the projects you build in Spala from the hosted dashboard.",
    "section": "general",
    "url": "https://docs.spala.ai/deployment/",
    "last_updated": "2026-07-08",
    "content": "Project Delivery\n\nSpala users deliver the projects they build: the generated API, static frontend, API docs, and integration settings. You use Spala from the hosted product; you do not run the Spala platform yourself. In these docs, deployment always means project delivery: a project artifact or generated project artifact, never the Spala dashboard, builder, or platform source code.\n\nUser project, not Spala platform\n\n  These docs are about getting your Spala project live. Platform hosting, dashboard delivery, builder runtime, and operational updates are managed by Spala.\n\nWhat You Can Ship\n\nPublished API\nReview project changes and publish the generated backend API.\n/features/publish-export\n\nFrontend App\nHost a static frontend next to the backend when your project needs a simple UI.\n/features/frontend-app\n\nAPI Docs\nShare Swagger, Markdown, OpenAPI JSON, SDK, and Webflow snippets.\n/features/api-docs\n\nCI/CD\nConnect generated project deployment to your server, deploy hook, or external workflow.\n/features/ci-cd\n\nDelivery Actions\n\n| Action | What changes | Who manages it | Where it lives |\n| --- | --- | --- | --- |\n| Publish backend API | Live generated API behavior for the project | You review and publish; Spala serves the hosted API | Project API base URL |\n| Publish frontend app | Static frontend files uploaded to the project | You or an agent upload, preview, and publish | Project frontend URL |\n| Share API docs | Generated Markdown, JSON, OpenAPI, SDK, or snippets | You choose what to expose or share | Settings → API Docs and generated docs routes |\n| Deploy generated project artifact | Generated project runtime sent to an external server or deploy provider | You configure target credentials and operations | Your infrastructure |\n\nWhat You Manage\n\n- Project resources: tables, endpoints, functions, tasks, triggers, agents, channels, addons, and settings\n- Project environment variables and integration credentials\n- Published API behavior and generated API docs\n- Frontend app assets when you use Spala frontend hosting\n- Agent access through public MCP discovery and the selected project MCP\n\nWhat Spala Manages\n\n- Hosted dashboard and builder runtime\n- Platform authentication and account access\n- Managed project infrastructure used by the hosted experience\n- Platform updates and operational maintenance\n\nStart a Project\n/deployment/start\nCreate a project from the hosted dashboard\nPublish\n/deployment/publish\nPublish generated backend changes\nSettings\n/settings\nConfigure project-level tools\nSpala Public MCP\n/agents/mcp\nLet agents discover, authenticate, and hand off to projects"
  },
  {
    "path": "/reference",
    "title": "Reference",
    "description": "Complete reference documentation for Spala step types, filters, operators, and data types.",
    "section": "general",
    "url": "https://docs.spala.ai/reference/",
    "last_updated": "2026-07-08",
    "content": "Reference\n\nDetailed reference documentation for every building block in Spala.\n\nSteps\n/reference/steps\n\n    Step types organized by category: variables, database, control flow, API, auth, crypto, file, encoding, and more.\n\nFilters\n/reference/filters\n\n    Data transformation filters for strings, arrays, numbers, dates, objects, and type conversions. Applied using the pipe operator.\n\nOperators\n/reference/operators\n\n    Arithmetic, comparison, logical, ternary, nullish coalescing, and optional chaining operators for expressions.\n\nData Types\n/reference/data-types\n\n    Supported data types: string, number, boolean, null, array, object, date, and buffer.\n\nFunctions\n/flows\nBuild backend logic using steps, expressions, and filters\nQuick Start\n/getting-started/quickstart\nSee steps and filters in action with a hands-on tutorial"
  },
  {
    "path": "/settings",
    "title": "Settings",
    "description": "Configure project metadata, database, auth, AI, MCP, frontend hosting, API docs, environment variables, snapshots, and project controls.",
    "section": "general",
    "url": "https://docs.spala.ai/settings/",
    "last_updated": "2026-07-08",
    "content": "Settings\n\nSettings is the control panel for project-level behavior. Open it from the sidebar in Advanced mode, or from the Lite mode tools menu.\n\nSettings Areas\n\nGeneral\nProject name, API docs, project controls, and editor preferences.\n/settings\n\nDatabase\nDatabase copy, backup, restore, connection, and data management controls.\n/database\n\nSecurity\nSecurity policy, CORS origins, trusted hosts, tokens, and request controls.\n/settings/cors-security\n\nUsers & Roles\nBuilder users, roles, permissions, and access control.\n/features/team-access\n\nAI\nProvider configuration, usage limits, data access controls, and AI instructions.\n/features/ai-assistant\n\nProject Memory\nSaved project memory that AI can search, pin, approve, edit, or archive.\n/features/project-memory\n\nMCP Server\nProject MCP access for desktop AI tools, install manifests, scopes, and connection instructions.\n/agents/mcp\n\nPublishing\nReview and publish project changes as a live API.\n/features/publish-export\n\nFrontend App\nFrontend hosting and generated app deployment settings.\n/features/frontend-app\n\nAPI Docs\nPublic API docs export and documentation behavior.\n/features/api-docs\n\nEnvironment\nRuntime environment variables used by flows, addons, and generated apps.\n/addons\n\nSnapshots\nProject snapshots for review, rollback, and publish history.\n/features/project-snapshots\n\nCI/CD\nDeploy generated backend bundles or trigger deployment hooks after publish.\n/features/ci-cd\n\nTrash Bin\nDeleted resources and recovery/cleanup controls.\n/features/team-access\n\nAdvanced\nRequest history, instance configuration, runtime tools, reload controls, and bulk cleanup.\n/settings\n\nAI Settings\n\nSpala AI settings showing provider and data access controls\nAI settings control provider access, usage limits, and which project data AI can read.\n\nUse AI settings to configure provider credentials, data access, and limits. Data access matters because Ask mode can answer questions using project context only when the relevant tables and fields are allowed. Project Memory adds durable instructions and decisions that keep future AI work aligned.\n\nMCP Settings\n\nSpala MCP settings showing server status, install manifest exposure, tools, scopes, and client choices\nMCP settings expose project MCP access and client-specific connection instructions.\n\nProject MCP is different from the public Spala MCP. The public MCP helps agents discover Spala, authenticate, select a project, and hand off. Project MCP is where authenticated agents inspect and operate one backend.\n\nDo not hardcode project MCP URLs\n\n  Let Spala return the project MCP URL from authenticated project data. Project URLs can vary by hosted domain, project slug, or explicit manifest configuration.\n\nEnvironment And Publishing\n\nSpala environment settings for runtime variables\nEnvironment variables feed addons, flows, generated frontends, and runtime integrations.\n\nEnvironment variables are the right place for API keys and runtime values used by your project. Addon pages list the variables each addon expects.\n\nAPI Docs, Snapshots, And CI/CD\n\nSpala API Docs settings showing public docs, SDK, OpenAPI, and snippets controls\nAPI Docs settings let you share the API contract with frontend builders and agents.\n\nSpala project snapshots settings showing project restore points\nSnapshots give you checkpoints before larger AI, agent, or manual changes.\n\nSpala CI/CD settings showing deployment target controls\nCI/CD connects generated project deployment to your server, deploy hook, or external workflow.\n\nProject Delivery Tools\n\nSettings also includes the operational tools around the generated backend:\n\n- Frontend App - upload, preview, publish, roll back, or let an agent package a static UI.\n- API Docs - share Swagger UI, Markdown docs, OpenAPI JSON, SDK files, and snippets with frontend builders.\n- Snapshots - create checkpoints and restore the full draft or selected resources after larger changes.\n- CI/CD - deploy generated bundles to your own targets or trigger external deploy hooks after publish.\n- Team Access - manage builder users, roles, permissions, security, and recovery controls.\n\nAdvanced And Recovery Controls\n\nThe General and Advanced tabs hold tools that are useful during review, migration, or recovery:\n\n- Export Project JSON - download a redacted project definition for backup, review, or support.\n- Download Runtime ZIP - generate a project runtime bundle for advanced deployment workflows.\n- Import Project JSON - restore or move a project definition when you intentionally need to.\n- API Docs Markdown / JSON - download documentation artifacts without enabling public docs.\n- Editor Preferences - set default view behavior for focused editing.\n- Request & Execution History - inspect API calls, function runs, errors, and timing.\n- Instance Configuration - view read-only CONFIG_* values available to the project runtime.\n- Danger Zone - bulk-delete functions, endpoints, tasks, triggers, channels, models, or addons when you are intentionally resetting a project.\n\nProject runtime, not the Spala platform\n\n  Advanced export and runtime tools are for the backend project you build in Spala. They do not package the hosted Spala product or replace the dashboard.\n\nLite Mode\n/features/lite-mode\nUse the Copilot workspace and tools menu\nProject Overview\n/features/project-overview\nInspect resources, project graph, drafts, and agents\nPublic MCP\n/agents/mcp\nConnect AI agents to Spala safely\nPublish\n/features/publish-export\nReview and publish your backend API\nFrontend App\n/features/frontend-app\nHost a static app UI for the project\nAPI Docs\n/features/api-docs\nShare endpoint docs and SDKs with frontend builders\nCORS and Security\n/settings/cors-security\nConfigure browser origins before frontend handoff"
  },
  {
    "path": "/start-here",
    "title": "Start Here",
    "description": "Choose the right first path for Spala: dashboard builder, frontend integration, AI agent, or raw MCP.",
    "section": "general",
    "url": "https://docs.spala.ai/start-here/",
    "last_updated": "2026-07-08",
    "content": "Start Here\n\nSpala is a hosted backend builder for AI-built apps. Use the hosted dashboard to create and publish project APIs. Connect AI coding agents through Spala MCP when an agent needs to discover projects, read context, or hand off into a project-specific MCP.\n\nPick the path that matches your job.\n\nBoundary glossary\n\n| Term | Meaning |\n| --- | --- |\n| Spala platform | The hosted dashboard, builder runtime, platform auth, updates, and managed infrastructure operated by Spala |\n| Project | The backend app you build in Spala: tables, functions, endpoints, tasks, triggers, channels, addons, settings, docs, and frontend assets |\n| Publish backend API | Promote the project draft to the live generated API that frontends and integrations call |\n| Deploy generated project artifact | Advanced CI/CD path for deploying a generated project artifact outside the default hosted publish flow |\n| Frontend App | Static UI assets hosted beside the generated project backend |\n| Project Agent | Backend automation that belongs to one Spala project and runs from chat, schedules, webhooks, data changes, or realtime events |\n| AI/MCP Agent | External coding agent or MCP client, such as Codex, Claude, Cursor, or VS Code, that helps build or inspect a project |\n| Public MCP | https://mcp.spala.ai/mcp, used for discovery, auth metadata, docs search, project lookup, and handoff |\n| Project MCP | The project-scoped MCP URL returned after auth and project selection; backend changes happen there |\n\nBefore you build\n\n  Start from the hosted dashboard at https://dashboard.spala.ai. New users should create an account from the dashboard sign-up screen. If you are joining an existing company workspace, accept the workspace invite or ask the workspace owner to add you. You also need permission to create or edit a project. A first Copilot-built API usually takes a few minutes; at the end you should have tested endpoints, a published API base URL, and generated API docs for frontend work.\n\nDelivery actions\n\n| Action | What changes | Who manages it | Where it lives |\n| --- | --- | --- | --- |\n| Publish backend API | Draft project resources become the live API | You approve publish; Spala serves the hosted project API | Project API base URL |\n| Publish frontend app | Static UI assets become the live project UI | You upload or agent-build the frontend; Spala hosts the assets | Project frontend URL |\n| Deploy generated project artifact | Generated project runtime is sent to an external target | You configure the target and credentials | Your server or deploy provider |\n| Use project MCP | An authenticated agent edits one selected project | You grant access; agent uses returned MCP URL | Project-scoped MCP endpoint |\n\nI am building in the dashboard\n\nUse this path when you want to create a backend from a prompt and publish it from Spala.\n\n1. Open https://dashboard.spala.ai.\n2. Create an account, sign in, or accept your workspace invite.\n3. Create or open a project.\n4. Start in Lite Mode from the project header or mode switch.\n5. Ask AI Copilot for the backend feature you need.\n6. Review generated tables, functions, endpoints, tasks, triggers, channels, and settings.\n7. Test in API Playground.\n8. Use Publish backend API to make the generated project API live.\n\nQuick Start\nBuild and publish a Cases API from the dashboard.\n/getting-started/quickstart\n\nAI Copilot\nDescribe the backend feature and review generated resources.\n/features/ai-assistant\n\nLite Mode\nUse the Copilot workspace and project graph as the first screen.\n/features/lite-mode\n\nI am connecting a frontend\n\nUse this path when you already have, or are building, a React, Next.js, Vite, Webflow, mobile, or external frontend.\n\n1. Find the project API base URL in Lite Mode or Project Overview.\n2. Open Settings → API Docs for the generated Markdown, JSON, OpenAPI, or SDK contract.\n3. Find routes in Endpoints or the generated API Docs.\n4. Use /api/... route paths exactly as published.\n5. For protected endpoints, send Authorization: Bearer USER_JWT.\n6. Store environment values in your frontend as VITE_SPALA_API_BASE_URL or NEXT_PUBLIC_SPALA_API_BASE_URL.\n7. Test calls in API Playground before wiring the frontend.\n\nFrontend Integration Cookbook\nCopyable examples for base URLs, auth, uploads, realtime, and env vars.\n/guides/frontend-integration\n\nExternal Frontend Handoff\nShare the exact packet a frontend developer or AI agent needs without dashboard access.\n/guides/frontend-handoff\n\nAPI Endpoints\nCreate and inspect REST API routes.\n/endpoints\n\nAPI Docs\nExport OpenAPI, Markdown docs, SDK files, and snippets.\n/features/api-docs\n\nI am an AI coding agent\n\nUse this path when Codex, Claude Code, Cursor, VS Code, or another agent needs to work with Spala.\n\n1. Connect to the public MCP server at https://mcp.spala.ai/mcp.\n2. Call spala_get_onboarding.\n3. Call spala_get_tool_map.\n4. Use public tools for docs, templates, and addons.\n5. When project access is needed, complete Spala platform OAuth.\n6. Use project_list and project_select.\n7. Switch to the exact project MCP URL returned by Spala.\n8. Inspect project context before making changes.\n9. Preview, validate, apply, publish backend API when requested, and review through the project MCP.\n\nDo not guess project MCP URLs\n\n  Project MCP URLs are resolved after authentication from Spala project data. Do not hardcode https://api.spala.ai/{{project}}/mcp or any other pattern.\n\nSpala Public MCP\nPublic MCP tools, OAuth metadata, raw transcript, and project handoff.\n/agents/mcp\n\nCodex Setup\nAdd Spala Public MCP to Codex.\n/agents/codex\n\nCursor Setup\nConfigure Cursor with the public MCP entrypoint.\n/agents/cursor\n\nMore MCP Clients\nGemini CLI, VS Code/Copilot, Windsurf, and Cline setup paths.\n/agents\n\nPublic Agent Skills\nSkill files that help agents evaluate Spala and start safely.\n/agents/skills\n\nI am implementing raw MCP\n\nUse this path only if you are building an MCP client or debugging MCP transport directly.\n\n1. Use POST https://mcp.spala.ai/mcp.\n2. Send Content-Type: application/json.\n3. Send Accept: application/json, text/event-stream.\n4. Call MCP protocol methods such as initialize, tools/list, and tools/call.\n5. Pass Spala tool names inside tools/call; do not call tool names as top-level JSON-RPC methods.\n6. Discover OAuth endpoints from MCP OAuth metadata.\n\nSpala Public MCP\n/agents/mcp\nFull raw MCP notes and transcript\nProject Delivery\n/deployment\nPublish projects without self-hosting Spala itself\nSettings\n/settings\nConfigure API docs, auth, env vars, MCP, and publishing"
  },
  {
    "path": "/tasks",
    "title": "Tasks",
    "description": "Run logic on a schedule or in the background.",
    "section": "general",
    "url": "https://docs.spala.ai/tasks/",
    "last_updated": "2026-07-08",
    "content": "Tasks\n\nRun logic on a schedule or in the background for jobs that should not block an API response.\n\nThe Tasks section showing scheduled and background tasks in the sidebar\nView and manage all your tasks in one place\n\nHow to create a scheduled task\n\nOpen the Tasks screen\n\n    Go to Tasks from the sidebar and click New Task.\n\nSet how it runs\n\n    Give your task a name. Add a frequency such as every 5 minutes, every hour, or every day, or leave it as a manual task that only runs on demand.\n\nBuild the function\n\n    Use the same function editor you use for endpoints. Add database queries, API calls, email sends, or any other backend steps the task needs.\n\nGood to know\n\n  Tasks can run in the main API process or in a background process with resource limits. Use background execution for slower jobs that should not block normal API traffic.\n\nLearn more\n\nScheduled Tasks\nSet repeat frequency, manual runs, background execution, and resource limits.\n/tasks/scheduled-tasks\n\nBackground Tasks\nRun long operations without blocking your API responses.\n/tasks/background-tasks\n\nScheduled Jobs Guide\n/guides/scheduled-jobs\nBuild cleanup routines and periodic syncs\nTriggers\n/triggers\nAuto-run logic when data changes instead of on a schedule"
  },
  {
    "path": "/triggers",
    "title": "Triggers",
    "description": "Auto-run logic when your data changes — send emails on signup, update inventory on orders, and more.",
    "section": "general",
    "url": "https://docs.spala.ai/triggers/",
    "last_updated": "2026-07-08",
    "content": "Triggers\n\nAuto-run logic when your data changes — send a welcome email when a user signs up, update inventory when an order is placed.\n\nThe Triggers section showing trigger list in the sidebar\nManage all your triggers in one place\n\nWhat triggers do\n\nA trigger watches for a specific event, like a row being inserted or updated, and automatically runs a function when it happens. You set it up once, and it fires every time.\n\nExamples:\n\n- A new user signs up → send a welcome email\n- An order is placed → reduce inventory count\n- A record is updated → log the change for auditing\n- A custom event fires → notify connected clients\n\nHow to create a trigger\n\nOpen the Triggers screen\n\n    Go to Triggers from the sidebar and click New Trigger.\n\nChoose the table and events\n\n    Pick the table and the insert, update, or delete events that should fire the trigger.\n\nBuild the function\n\n    Use the function editor to define what happens when the trigger fires. You have access to the operation plus old and new row data, so you can react to specific changes.\n\nPro tip\n\n  Use the sync/async setting to decide whether the trigger should run inline with the data change or as follow-up work.\n\nTrigger types\n\nDatabase Triggers\nRun functions when rows are inserted, updated, or deleted.\n/triggers/database-triggers\n\nEvent Triggers\nAvailability note for event-driven workflow alternatives.\n/triggers/event-triggers\n\nScheduled Tasks\n/tasks\nRun logic on a schedule instead of on data changes\nBuild a REST API\n/guides/build-rest-api\nCreate endpoints that can fire triggers"
  },
  {
    "path": "/getting-started",
    "title": "Welcome to Spala",
    "description": "Spala builds backend infrastructure for AI-built apps: database, auth, REST APIs, logic, docs, publishing, and MCP access for coding agents.",
    "section": "general",
    "url": "https://docs.spala.ai/getting-started/",
    "last_updated": "2026-07-08",
    "content": "Welcome to Spala\n\nSpala is a backend automation platform for AI-built apps. Describe what your product needs and Spala helps create the database, auth, REST APIs, validation, backend logic, docs, and publishing flow.\n\nYou can work in Lite mode with the Copilot and project graph, open Advanced mode for full resource editing, switch into code, or connect an approved AI coding agent through Spala MCP.\n\nThe Spala Lite workspace showing AI Copilot and the project graph\nStart from the Copilot workspace and inspect the generated project graph.\n\nHow people build with Spala\n\nMost users start with the AI Copilot. Describe a feature in plain English, and the AI generates your database tables, functions, and API endpoints. Then fine-tune with the visual editor or switch to JavaScript for full control.\n\nLite Mode\nStart from the Copilot workspace, inspect the project graph, and jump into focused tools.\n/features/lite-mode\n\nAI Copilot\nDescribe what you want and the AI builds it. The fastest way to create features.\n/features/ai-assistant\n\nAI Agent MCP\nConnect Codex, Claude, Cursor, or another MCP client to discover projects and hand off to the project MCP.\n/agents/mcp\n\nFrontend App\nPublish a static UI next to your backend, or give an agent upload instructions.\n/features/frontend-app\n\nFunction Editor\nUse Gherkin, Visual, Split, and Code views to inspect and refine backend behavior.\n/flows\n\nCode View\nEvery function can be edited as JavaScript and kept connected to visual views.\n/flows/code-view\n\nAddons\nStripe, OpenAI, SendGrid, S3, Twilio, Slack, and more — plug in what you need.\n/addons\n\nWho is Spala for?\n\n- Indie developers who want to ship APIs fast without boilerplate\n- Startups that need to prototype and iterate quickly\n- Agencies building custom backends for clients\n- Beginners who want real APIs without learning a framework\n- AI coding agents that need a safe backend builder and project handoff flow\n\nKey features\n\n- Lite mode — Start from the Copilot workspace, inspect the project graph, and jump into focused tools\n- AI Copilot — Describe features in English, get a complete backend in seconds\n- Public MCP — Agents start at https://mcp.spala.ai/mcp, authenticate with Spala, select a project, then switch to the returned project MCP\n- Function editor — Use Gherkin, Visual, Split, and Code views to build backend logic.\n- Addons — Payments, email, file storage, AI models, SMS, and more\n- Code view — Edit visually or in JavaScript while preserving the function structure.\n- Project graph — See the database, APIs, functions, tasks, triggers, and channels the Copilot created\n- Database designer — Create tables and relationships visually\n- Project agents — Run project-native automations from chat, webhooks, schedules, data changes, or realtime events\n- Try & Runs — Set breakpoints, inspect variables, and review execution history\n- Settings — Configure AI, MCP, auth, API docs, environment variables, team access, snapshots, CI/CD, and frontend hosting\n- Publish — Review changes and make your generated API live\n\nReady to build?\n\n  Jump to the Quick Start to build a Cases API in 5 minutes. If you are using an AI coding assistant, connect Spala Public MCP first.\n\nQuick Start\n/getting-started/quickstart\nBuild a Cases API from scratch\nSpala Public MCP\n/agents/mcp\nConnect an AI coding assistant to Spala\nAI Copilot\n/features/ai-assistant\nThe fastest way to build with Spala\nLite Mode\n/features/lite-mode\nUse the Copilot workspace and project graph\nAddons\n/addons\nIntegrations for payments, email, AI, storage, and more\nThe Interface\n/getting-started/interface\nA visual tour of the Spala UI"
  },
  {
    "path": "/getting-started/quickstart",
    "title": "Quick Start: Build a Cases API",
    "description": "Create a working REST API for a client portal from the hosted Spala dashboard.",
    "section": "getting-started",
    "url": "https://docs.spala.ai/getting-started/quickstart/",
    "last_updated": "2026-07-08",
    "content": "Quick Start: Build a Cases API\n\nThe fastest way to build in Spala is to start with AI Copilot. This guide starts with the Copilot path, then shows the same pieces manually so you understand what Spala creates. Examples use /api/... paths so the published API is easy to recognize from client code.\n\nHosted dashboard first\n\n  Start from the hosted Spala dashboard at https://dashboard.spala.ai. Most users can describe the whole feature to the AI Copilot first; this guide shows the manual editor tour so you understand the pieces Spala creates.\n\nBefore you begin\n\n  Start from https://dashboard.spala.ai. New users should create an account from the dashboard sign-up screen. If you are joining an existing company workspace, accept the workspace invite first. You also need permission to create or edit a project. This guide should leave you with a tested GET /api/cases endpoint, a published project API base URL, and generated API Docs you can hand to a frontend developer or AI coding agent.\n\n1. Create a project\n\nOpen Spala\n\n    Open https://dashboard.spala.ai and create an account or sign in. If you are joining an existing company workspace, accept the invite first.\n\nCreate a new project\n\n    Click New Project on the dashboard. Name it \"Legal Client Portal API\" and click Create.\n\n2. Fast path: ask AI Copilot\n\nOpen Lite Mode\n\n    Lite Mode keeps AI Copilot and the project graph visible together. If the project opens in Advanced mode, use the header mode switch to move to Lite Mode.\n\nDescribe the feature\n\n    Ask Copilot: \"Create a cases API for a legal client portal with a cases table, GET /api/cases list endpoint, and POST /api/cases/:id/status update endpoint.\"\n\nReview the result\n\n    Check the generated tables, functions, endpoints, and project graph. Use Ask mode if you want Copilot to explain what it created.\n\nTest and publish\n\n    Test the endpoint in Playground, then click Publish when the project draft is ready to become the live API.\n\nManual tour below\n\n  The rest of this guide shows the manual path through Database, Functions, Endpoints, Playground, and Publish. Use it when you want to understand or directly edit the resources Copilot creates.\n\n3. Create the cases table\n\nOpen the Database section\n\n    Click Database in the left sidebar.\n\nAdd a table\n\n    Click Add Table and name it cases. Spala creates an id column automatically.\n\nAdd fields\n\n    Add these fields to your cases table:\n\n    | Field Name  | Type            | Required |\n    |-------------|-----------------|----------|\n    | case_number | Text            | Yes      |\n    | case_title  | Text            | Yes      |\n    | status      | Text            | Yes      |\n    | client_id   | Table Reference | No       |\n    | attorney_id | Table Reference | No       |\n    | created_at  | Timestamp       | No       |\n\nSave\n\n    Click Save. Your table is saved in the project draft and is ready to use in functions.\n\nThe table creation entry point in Spala\nCreate a table from the Database screen.\n\n4. Build a \"List Cases\" function\n\nGo to Functions\n\n    Click Functions in the sidebar, then Create Function. Name it \"List Cases\".\n\nAdd a Database Query step\n\n    Open Visual view and add a Database Query step. Configure it:\n\n    - Table: cases\n    - Operation: Select\n    - Assign To: cases\n\nAdd a Return step\n\n    Add a Return step after the query. Set its value to:\n\n    vars.cases\n\n5. Create the GET /api/cases endpoint\n\nGo to Endpoints\n\n    Click Endpoints in the sidebar, then Add Endpoint.\n\nConfigure it\n\n    - Method: GET\n    - Path: /api/cases\n    - Function: List Cases\n    - Authentication: None\n\nSave\n\n    Click Save. Your endpoint is saved in the project draft and ready to test.\n\n6. Test it in the Playground\n\nOpen the Playground\n\n    Click Playground in the sidebar. Select your GET /api/cases endpoint.\n\nSend a request\n\n    Click Send. You will see an empty array [] since there is no case data yet.\n\nAdd some cases\n\n    Go back to Database, click the cases table, and use the Data tab to add a few rows. Then test the endpoint again.\n\n7. Publish it\n\nOpen Publish\n\n    Click Publish in the header when the table, function, and endpoint look right.\n\nReview the changes\n\n    Review the pending changes, then publish. After publishing, your endpoint is available from your project's API base URL.\n\nThe Spala publish dialog\nReview pending changes before publishing your backend.\n\n8. Confirm the live API\n\nAfter publish, complete this checklist before giving the backend to a frontend builder:\n\n1. Copy the project API base URL from Lite Mode or Project Overview.\n2. Open Settings → API Docs and confirm GET /api/cases appears.\n3. Test the live endpoint from API Playground.\n4. Copy the generated Markdown docs, OpenAPI JSON, or SDK if another person or agent is building the frontend.\n5. Add your frontend origin in Settings → Security → CORS Allowed Origins if browser calls are blocked.\n\nFirst frontend call:\n\nconst API_BASE_URL = import.meta.env.VITE_SPALA_API_BASE_URL;\n\nconst response = await fetch(`${API_BASE_URL}/api/cases`);\nif (!response.ok) {\n  throw new Error(`Spala API error: ${response.status}`);\n}\n\nconst cases = await response.json();\n\nBonus: Add an \"Update Case Status\" endpoint\n\nTake it further by creating a POST /api/cases/:id/status endpoint:\n\nCreate a new function\n\n    Add a function called \"Update Case Status\".\n\nAdd a Database Query step\n\n    Configure it:\n\n    - Table: cases\n    - Operation: Update\n    - Set: status to input.status\n    - Where: id = params.id\n    - Assign To: updatedCase\n\nReturn the result\n\n    Add a Return step with value vars.updatedCase.\n\nCreate the endpoint\n\n    Add a POST endpoint at /api/cases/:id/status, attach the function, save it, test it, then publish when it is ready.\n\nPro tip\n\n  You can build the whole feature with the AI Copilot. Describe what you want: \"Create a cases API for a legal client portal with GET /api/cases and POST /api/cases/:id/status endpoints.\"\n\nUsing an AI coding agent\n\nIf you want Codex, Claude, Cursor, or another MCP client to help with the backend, connect the public Spala MCP first:\n\ncodex mcp add spala-public --url \"https://mcp.spala.ai/mcp\"\n\nThe public MCP is for discovery, OAuth metadata, project lookup, and handoff. After authentication, let Spala return the selected project MCP URL. Do not hardcode a URL such as https://api.spala.ai/{project}/mcp.\n\nWhat's next?\n\nSpala Public MCP\n/agents/mcp\nConnect an AI coding assistant to Spala\nThe Interface\n/getting-started/interface\nLearn your way around the Spala UI\nDatabase\n/database\nTables, fields, and relationships\nFunctions\n/flows\nAll the ways to build backend logic\nAPI Endpoints\n/endpoints\nMethods, paths, authentication, and more"
  },
  {
    "path": "/getting-started/interface",
    "title": "The Interface",
    "description": "A visual tour of the Spala UI — learn where everything is and what each section does.",
    "section": "getting-started",
    "url": "https://docs.spala.ai/getting-started/interface/",
    "last_updated": "2026-07-08",
    "content": "The Interface\n\nThis page gives you a quick tour of the Spala UI so you know where everything lives.\n\nDashboard\n\nWhen you open a project, Lite mode shows the Copilot next to your project graph. This is where most users describe what they want, inspect what Spala built, and jump into focused editors.\n\nThe Spala Lite workspace showing AI Copilot and the project graph\nLite mode keeps the Copilot and project graph side by side.\n\nAdvanced mode opens on Project Overview. Use the tabs there to scan backend resources, inspect the project network, and run or review project agents before you open a focused editor.\n\nThe Spala Project Overview showing backend resources and endpoint summary\nProject Overview shows the live shape of your backend and the most common jump points.\n\nSidebar\n\nThe left sidebar is your main navigation. Here is what each section does:\n\n| Section       | What it does                                                |\n|--------------|-------------------------------------------------------------|\n| Database  | Design tables, add fields, set up relationships             |\n| Functions | Build backend logic with Gherkin, Visual, Split, and Code views |\n| Endpoints | Create REST API routes and attach functions to them          |\n| Tasks     | Set up functions that run manually, in the background, or on a repeat schedule |\n| Triggers  | Auto-run functions when database records change              |\n| Channels  | Manage realtime channels when enabled for the project        |\n| Agents    | Build project-native automations with triggers, chat, timeline, and metrics |\n| Pull Requests | Review generated backend changes before merging or publishing |\n| Background Tasks | Monitor queued, running, failed, and completed async jobs |\n| Addons    | Browse and enable addons for email, payments, AI, and more  |\n| Playground| Test your endpoints with sample requests                    |\n\nThe Editor\n\nWhen you open a function, endpoint, or table, the editor takes over the main area. Function editing starts in Gherkin view:\n\nThe Spala Gherkin view showing a function outline and typed inputs\nThe Gherkin view shows the function contract, outline, and response shape.\n\nFunction editors have several views:\n\n- Gherkin - A readable outline of inputs, outputs, preparation, business logic, and response shape.\n- Visual - Editable step blocks and inputs for users who want direct control without writing code.\n- Split - Visual and Code together when you need both context and detail.\n- Code - JavaScript for advanced editing.\n\nThe editor workspace also gives you open tabs, view-mode memory, Ask AI, Try, Compare, Discard, Save, version history, run history, export, revert, and delete controls.\n\nCode View\n\nEvery function has a Code tab next to Gherkin, Visual, and Split. Click it to see and edit the JavaScript version of your function. Changes stay connected to the visual views.\n\nThe code view showing JavaScript\nSwitch between visual and code at any time\n\nGood to know\n\n  You do not have to choose one or the other. Build visually, then fine-tune in code — or write code and see it appear as visual steps.\n\nPublish\n\nWhen your project is ready, click Publish to publish the backend API. Your endpoints become accessible at your configured project API base URL. This publishes project API behavior; it does not deploy or self-host the Spala platform.\n\nSearch everything\n\nPress Cmd+K (or Ctrl+K on Windows) to open the search palette. It searches across your tables, functions, endpoints, tasks, triggers, and settings. Use type prefixes to filter:\n\n| Prefix | Searches |\n|--------|----------|\n| @t, @table | Tables |\n| @f, @fn, @function | Functions |\n| @e, @endpoint | Endpoints |\n| @tk, @task | Tasks |\n| @tr, @trigger | Triggers |\n| @ch, @channel | Channels |\n\nSettings and delivery tools\n\nSettings is where project-level operations live: AI configuration, Project Memory, MCP, Frontend App hosting, generated API Docs, Environment variables, Snapshots, CI/CD, Team Access, and Trash Bin recovery.\n\nKeyboard shortcuts\n\n| Shortcut | Action |\n|----------|--------|\n| Cmd/Ctrl + K | Search palette |\n| Cmd/Ctrl + J | AI Copilot |\n| Cmd/Ctrl + L | Log terminal |\n| Escape | Close panel |\n\nTrash bin\n\nDeleted items go to the Trash Bin (in Settings). You can preview and restore anything you accidentally deleted.\n\nQuick Start\n/getting-started/quickstart\nBuild your first API\nProject Overview\n/features/project-overview\nInspect the whole backend from one screen\nEditor Workspace\n/features/editor-workspace\nUse tabs, search, view modes, Ask AI, Try, and save controls\nDatabase\n/database\nDesign your data model\nFunctions\n/flows\nStart building backend logic\nFrontend App\n/features/frontend-app\nPublish a UI for the project"
  },
  {
    "path": "/guides/stripe-payments",
    "title": "Accept Payments with Stripe",
    "description": "Install the Stripe addon, create a checkout endpoint, and handle webhooks.",
    "section": "guides",
    "url": "https://docs.spala.ai/guides/stripe-payments/",
    "last_updated": "2026-07-08",
    "content": "Accept Payments with Stripe\n\nSet up Stripe checkout in your API — create payment sessions, redirect users to pay, and track order status with webhooks.\n\nWhat you will build\n\n- POST /api/checkout — create a Stripe checkout session\n- POST /api/webhooks/stripe — handle payment status updates\n\nSteps\n\nInstall the Stripe addon\n\n    Go to Addons, find Stripe, and click Install. Then add your Stripe secret key as STRIPE_SECRET_KEY in Settings > Environment Variables.\n\nCreate an orders table\n\n    In the Database screen, create an orders table:\n\n    | Column | Type | Notes |\n    |--------|------|-------|\n    | id | Serial | Primary key |\n    | user_id | Integer | Foreign key to users |\n    | stripe_session_id | Text | Links to Stripe |\n    | status | Text | Default: 'pending' |\n    | total | Decimal | Order amount |\n    | created_at | Timestamp | Default: now() |\n\nBuild the checkout endpoint\n\n    Create POST /api/checkout with this function:\n\n    1. External API Call — Method: POST, URL: 'https://api.stripe.com/v1/checkout/sessions', Auth: Bearer env.STRIPE_SECRET_KEY, Content Type: form-encoded, Body: { mode: 'payment', success_url: input.successUrl, cancel_url: input.cancelUrl, 'line_items[0][price_data][currency]': 'usd', 'line_items[0][price_data][product_data][name]': input.productName, 'line_items[0][price_data][unit_amount]': input.amountCents, 'line_items[0][quantity]': input.quantity | default(1) }, Assign To: session\n    2. Database Insert — Table: orders, Data: { user_id: vars.auth.userId, stripe_session_id: vars.session.body.id, status: 'pending', total: input.total }\n    3. Respond — Status: 200, Body: { url: vars.session.body.url }\n\n    Your frontend redirects the user to the returned URL to complete payment.\n\nBuild the webhook endpoint\n\n    Create POST /api/webhooks/stripe. Stripe calls this URL when payment status changes.\n\n    1. Verify webhook signature — Validate the Stripe signature header with the webhook signing secret before trusting the payload.\n    2. Set Variable — Name: event, Value: input\n    3. Switch — Expression: vars.event.type\n       - Case 'checkout.session.completed': Database Update — Table: orders, Data: { status: 'paid' }, Where: { stripe_session_id: vars.event.data.object.id }\n       - Case 'charge.refunded': Database Update — Table: orders, Data: { status: 'refunded' }, Where: { stripe_session_id: vars.event.data.object.id }\n    4. Respond — Status: 200, Body: { received: true }\n\nConfigure the webhook in Stripe\n\n    In your Stripe dashboard, go to Developers > Webhooks, add your /api/webhooks/stripe URL, and select the events checkout.session.completed and charge.refunded.\n\nImportant\n\n  Webhook signature verification must happen before you update orders. Use the signing secret from your Stripe dashboard and reject requests that fail verification.\n\nAddons\n/addons\nBrowse all available integrations\nBuild a REST API\n/guides/build-rest-api\nCreate the product endpoints to sell"
  },
  {
    "path": "/guides/ai-integration",
    "title": "Add AI Features",
    "description": "Build a chat completion endpoint and add AI-powered features with the OpenAI addon.",
    "section": "guides",
    "url": "https://docs.spala.ai/guides/ai-integration/",
    "last_updated": "2026-07-08",
    "content": "Add AI Features\n\nBuild AI-powered endpoints — chat, summarization, content moderation — using the OpenAI addon.\n\nWhat you will build\n\n- POST /api/ai/chat — send a message and get an AI response\n- POST /api/ai/summarize — summarize a block of text\n- A content moderation pattern for user submissions\n\nSteps\n\nInstall the OpenAI addon\n\n    Go to Addons and install OpenAI. Add your API key as OPENAI_API_KEY in Settings > Environment Variables.\n\nBuild a chat endpoint\n\n    Create POST /api/ai/chat:\n\n    1. External API Call — Method: POST, URL: 'https://api.openai.com/v1/responses', Auth: Bearer env.OPENAI_API_KEY, Body:\n\n    {\n      \"model\": \"<current-openai-model>\",\n      \"instructions\": \"You are a helpful assistant.\",\n      \"input\": \"input.message\"\n    }\n\n    Assign To: aiResponse\n\n    2. Set Variable — Name: reply, Value: vars.aiResponse.body.output_text\n    3. Respond — Status: 200, Body: { reply: vars.reply }\n\n    Send { \"message\": \"What is Spala?\" } to test it.\n\nBuild a summarization endpoint\n\n    Create POST /api/ai/summarize — same pattern, different system prompt:\n\n    1. External API Call — same setup, but with a different instruction:\n\n    {\n      \"model\": \"<current-openai-model>\",\n      \"instructions\": \"Summarize the following text in 2-3 sentences.\",\n      \"input\": \"input.text\"\n    }\n\n    Assign To: result\n\n    2. Respond — Body: { summary: vars.result.body.output_text }\n\nAdd content moderation\n\n    Before saving user-generated content, check it with OpenAI's moderation endpoint:\n\n    1. External API Call — URL: 'https://api.openai.com/v1/moderations', Body: { input: input.content }, Assign To: moderation\n    2. If/Else — Condition: vars.moderation.body.results | first | get('flagged') == true\n       - True: Respond — Status: 400, Body: { error: 'Content violates community guidelines' }\n       - False: proceed with saving the content\n\nHandle errors gracefully\n\n    Wrap API calls in a Try/Catch step. In the Catch branch, check the status code — 429 means rate-limited (tell the user to retry), and 500+ means a provider error (return a generic error message).\n\nPro tip\n\n  The same pattern works with other AI providers. Swap the URL and authentication to use Anthropic, Mistral, or any OpenAI-compatible API.\n\nAddons\n/addons\nBrowse AI and other integrations\nBuild a REST API\n/guides/build-rest-api\nCreate the API structure for your AI features"
  },
  {
    "path": "/guides/user-auth",
    "title": "Add User Authentication",
    "description": "Set up signup, login with JWT tokens, and protect your endpoints.",
    "section": "guides",
    "url": "https://docs.spala.ai/guides/user-auth/",
    "last_updated": "2026-07-08",
    "content": "Add User Authentication\n\nSet up user registration, login, and JWT-based endpoint protection. By the end, you will have a working auth system you can add to any project.\n\nWhat you will build\n\n- POST /api/auth/register — create a new user account\n- POST /api/auth/login — sign in and receive a JWT token\n- A reusable pattern for protecting any endpoint\n\nSteps\n\nCreate the users table\n\n    In the Database screen, create a users table:\n\n    | Column | Type | Notes |\n    |--------|------|-------|\n    | id | Serial | Primary key |\n    | email | Text | Unique, required |\n    | password_digest | Text | Required |\n    | name | Text | Optional |\n    | role | Text | Default: 'user' |\n    | created_at | Timestamp | Default: now() |\n\nBuild the register endpoint\n\n    Create POST /api/auth/register:\n\n    1. Find One — Table: users, Where: { email: input.email }, Assign To: existing\n    2. If/Else — Condition: vars.existing != null\n       - True: Respond — Status: 409, Body: { error: 'Email already registered' }\n    3. Hash Password — Password: input.password, Salt Rounds: 12, Assign To: hashedPassword\n    4. Database Insert — Table: users, Data: { email: input.email, name: input.name, password_digest: vars.hashedPassword }, Returning: id, email, name, Assign To: user\n    5. Respond — Status: 201, Body: { data: vars.user }\n\nBuild the login endpoint\n\n    Create POST /api/auth/login:\n\n    1. Find One — Table: users, Where: { email: input.email }, Assign To: user\n    2. If/Else — Condition: vars.user == null\n       - True: Respond — Status: 401, Body: { error: 'Invalid credentials' }\n    3. Verify Password — Password: input.password, Hash: vars.user.password_digest, Assign To: isValid\n    4. If/Else — Condition: vars.isValid == false\n       - True: Respond — Status: 401, Body: { error: 'Invalid credentials' }\n    5. Generate Auth Token — User ID: vars.user.id, Assign To: token\n    6. Respond — Status: 200, Body: { token: vars.token }\n\nProtect an endpoint\n\n    The usual production path is to set the endpoint's Authentication option to JWT in the endpoint editor. Spala then rejects missing or invalid tokens before the function runs and exposes decoded auth data to the function.\n\n    Use manual verification steps only when an endpoint intentionally accepts mixed public/authenticated traffic or needs to verify a secondary token. For that custom case, add these steps at the top of its function:\n\n    1. Set Variable — Name: authHeader, Value: headers.authorization\n    2. If/Else — Condition: vars.authHeader == null\n       - True: Respond — Status: 401, Body: { error: 'No token provided' }\n    3. Try/Catch\n       - Try: Verify JWT — Token: vars.authHeader | split(' ') | last, Secret: env.JWT_SECRET, Assign To: auth\n       - Catch: Respond — Status: 401, Body: { error: 'Invalid token' }\n\n    After this, vars.auth.userId and vars.auth.role are available in the rest of the function.\n\nAdd the JWT secret\n\n    Go to Settings > Environment Variables and add JWT_SECRET with a long, random string.\n\nTest login\n\n    In the Playground, call POST /api/auth/register with an email and password. Then call POST /api/auth/login with the same credentials and confirm you get a token back.\n\nImportant\n\n  Never store plaintext passwords. Always use the Hash Password step with bcrypt before inserting into the database.\n\nBuild a REST API\n/guides/build-rest-api\nCreate the endpoints you want to protect\nAPI Playground\n/features/playground\nTest your auth endpoints in the browser"
  },
  {
    "path": "/guides/crud-operations",
    "title": "Advanced CRUD",
    "description": "Add pagination, filtering, sorting, and batch operations to your endpoints.",
    "section": "guides",
    "url": "https://docs.spala.ai/guides/crud-operations/",
    "last_updated": "2026-07-08",
    "content": "Advanced CRUD\n\nOnce you have basic CRUD endpoints, add pagination, filtering, and sorting to make them production-ready. This guide builds on the patterns from Build a REST API.\n\nSteps\n\nAdd pagination\n\n    Update your list endpoint to support ?page=1&limit=20:\n\n    1. Set Variable — Name: page, Value: query.page | toNumber | default(1)\n    2. Set Variable — Name: limit, Value: query.limit | toNumber | default(20) | clamp(1, 100)\n    3. Set Variable — Name: offset, Value: (vars.page - 1) * vars.limit\n    4. Count — Table: products, Assign To: total\n    5. Database Select — Table: products, Limit: vars.limit, Offset: vars.offset, Order By: created_at DESC, Assign To: products\n    6. Respond — Body: { data: vars.products, pagination: { page: vars.page, limit: vars.limit, total: vars.total, pages: (vars.total / vars.limit) | ceil } }\n\nAdd filtering\n\n    Build a dynamic Where clause from query parameters:\n\n    1. Set Variable — Name: where, Value: {}\n    2. If/Else — Condition: query.category != null\n       - True: Set Variable — Value: vars.where | set('category', query.category)\n    3. If/Else — Condition: query.minPrice != null\n       - True: Set Variable — Value: vars.where | set('price', { $gte: query.minPrice | toNumber })\n\n    Pass vars.where as the Where parameter in your Database Select step. Clients filter with ?category=electronics&minPrice=50.\n\nAdd sorting\n\n    Accept ?sort=price&order=asc:\n\n    1. Set Variable — Name: allowedSorts, Value: ['created_at', 'price', 'name']\n    2. If/Else — Condition: vars.allowedSorts | includes(query.sort | default('created_at'))\n       - False: Respond — Status: 400, Body: { error: 'Unsupported sort field' }\n    3. Set Variable — Name: sortField, Value: query.sort | default('created_at')\n    4. Set Variable — Name: sortOrder, Value: (query.order == 'asc') ? 'ASC' : 'DESC'\n    5. Set Variable — Name: orderBy, Value: vars.sortField + ' ' + vars.sortOrder\n\n    Use vars.orderBy in the Order By property of your Database Select step.\n\nBatch updates\n\n    Create PATCH /api/products/batch for updating multiple records:\n\n    1. Loop — Iterable: input.updates, Item Variable: update\n       - Database Update — Table: products, Data: vars.update.data, Where: { id: vars.update.id }\n    2. Respond — Status: 200, Body: { updated: input.updates | length }\n\nKeep sorting safe\n\n  Always validate sort columns against a whitelist. Do not pass arbitrary query parameter values into an Order By field.\n\nBuild a REST API\n/guides/build-rest-api\nStart with the basic CRUD setup\nAdd User Authentication\n/guides/user-auth\nProtect these endpoints"
  },
  {
    "path": "/guides/build-rest-api",
    "title": "Build a REST API",
    "description": "Create a complete REST API for case management with tables, endpoints, and CRUD operations.",
    "section": "guides",
    "url": "https://docs.spala.ai/guides/build-rest-api/",
    "last_updated": "2026-07-08",
    "content": "Build a REST API\n\nCreate a cases API with full CRUD operations — from database table to tested endpoints — in about 10 minutes.\n\nWhat you will build\n\nA cases API with five endpoints:\n\n- GET /api/cases — list all cases\n- GET /api/cases/:id — get a single case\n- POST /api/cases — create a case\n- PUT /api/cases/:id — update a case\n- DELETE /api/cases/:id — delete a case\n\nSteps\n\nCreate the cases table\n\n    Open the Database screen and create a new table called cases with these columns:\n\n    | Column | Type | Notes |\n    |--------|------|-------|\n    | id | UUID | Primary key |\n    | case_number | Text | Required, unique external reference |\n    | case_name | Text | Required client-facing title |\n    | description | Text | Summary of the matter |\n    | status | Text | e.g., open, review, closed |\n    | created_at | Timestamp | Default: now() |\n\nCreate the list endpoint\n\n    Go to Endpoints and create GET /api/cases. In the function editor, add two steps:\n\n    1. Database Select — Table: cases, Order By: created_at DESC, Assign To: cases\n    2. Respond — Status: 200, Body: { data: vars.cases }\n\n    Expected response:\n\n    { \"data\": [{ \"id\": \"8b6f2f8a-1c5a-4f3d-9b3f-9f9a8d1a2345\", \"case_number\": \"CASE-1042\", \"case_name\": \"Contract Review\", \"status\": \"open\" }] }\n\nCreate the get-by-id endpoint\n\n    Create GET /api/cases/:id with this function:\n\n    1. Find One — Table: cases, Where: { id: params.id }, Assign To: caseRecord\n    2. If/Else — Condition: vars.caseRecord == null\n       - True: Respond — Status: 404, Body: { error: 'Case not found' }\n       - False: Respond — Status: 200, Body: { data: vars.caseRecord }\n\nCreate the create endpoint\n\n    Create POST /api/cases:\n\n    1. Database Insert — Table: cases, Data: input, Returning: *, Assign To: newCase\n    2. Respond — Status: 201, Body: { data: vars.newCase }\n\n    Send this body to test:\n\n    { \"case_number\": \"CASE-1043\", \"case_name\": \"New Client Intake\", \"status\": \"open\" }\n\nCreate update and delete endpoints\n\n    Create PUT /api/cases/:id with a Database Update step (Where: { id: params.id }, Data: input) and a Respond step.\n\n    Create DELETE /api/cases/:id with a Database Delete step (Where: { id: params.id }) and a Respond step returning status 204.\n\nTest in the Playground\n\n    Open the Playground screen. Select POST /api/cases, enter a JSON body, and click Send. Then select GET /api/cases and verify your case appears in the list.\n\nPro tip\n\n  Add input validation by inserting an If/Else step before the database insert to check that input.case_number exists.\n\nAdvanced CRUD\n/guides/crud-operations\nAdd pagination, filtering, and sorting\nAdd User Authentication\n/guides/user-auth\nProtect your endpoints with JWT tokens\nAPI Playground\n/features/playground\nTest your endpoints in the browser"
  },
  {
    "path": "/guides/realtime-chat",
    "title": "Build Realtime Chat",
    "description": "Set up WebSocket channels, broadcast messages, and build a live chat backend.",
    "section": "guides",
    "url": "https://docs.spala.ai/guides/realtime-chat/",
    "last_updated": "2026-07-08",
    "content": "Build Realtime Chat\n\nBuild a chat backend where messages appear instantly for all connected users — using WebSocket channels and a message history endpoint.\n\nWhat you will build\n\n- A chat_messages table\n- POST /api/chat/send — send a message and broadcast it live\n- GET /api/chat/history — fetch recent messages\n- A WebSocket channel for live delivery\n\nSteps\n\nCreate the messages table\n\n    In the Database screen, create a chat_messages table:\n\n    | Column | Type | Notes |\n    |--------|------|-------|\n    | id | Serial | Primary key |\n    | channel | Text | Chat room name |\n    | user_id | Integer | Foreign key to users |\n    | username | Text | Display name |\n    | content | Text | Message body |\n    | created_at | Timestamp | Default: now() |\n\nCreate a channel\n\n    Go to the Channels screen and create a new channel called chat. This channel manages WebSocket subscriptions — clients subscribe and receive all events emitted to it.\n\nBuild the send message endpoint\n\n    Create POST /api/chat/send (protected with JWT auth):\n\n    1. Verify JWT — extract user info from the token\n    2. Database Insert — Table: chat_messages, Data: { channel: input.channel, user_id: vars.auth.userId, username: vars.auth.username, content: input.content }, Returning: *, Assign To: message\n    3. Emit Event — Event: new_message, Data: vars.message, Channel: 'chat_' + input.channel\n    4. Respond — Status: 201, Body: { data: vars.message }\n\n    The Emit Event step broadcasts the message to every connected client instantly.\n\nBuild the history endpoint\n\n    Create GET /api/chat/history:\n\n    1. Database Select — Table: chat_messages, Where: { channel: query.channel }, Order By: created_at DESC, Limit: 50, Assign To: messages\n    2. Set Variable — Name: reversed, Value: vars.messages | reverse\n    3. Respond — Status: 200, Body: { data: vars.reversed }\n\nConnect from the frontend\n\n    On the client side, open a WebSocket connection and subscribe:\n\n    const ws = new WebSocket('wss://your-project.spala.dev/ws');\n    ws.onopen = () => {\n      ws.send(JSON.stringify({ type: 'subscribe', channel: 'chat_general' }));\n    };\n    ws.onmessage = (event) => {\n      const data = JSON.parse(event.data);\n      if (data.event === 'new_message') {\n        // Append message to your chat UI\n      }\n    };\n\nAdd typing indicators (optional)\n\n    Create POST /api/chat/typing that emits a typing event without saving anything:\n\n    1. Emit Event — Event: typing, Data: { username: vars.auth.username }, Channel: 'chat_' + input.channel\n    2. Respond — Status: 200\n\nGood to know\n\n  Channel events are scoped by name — chat_general and chat_support operate independently. Use different names to create separate chat rooms.\n\nRealtime\n/channels\nLearn more about WebSocket channels\nAdd User Authentication\n/guides/user-auth\nSet up the auth system for your chat users"
  },
  {
    "path": "/guides/frontend-handoff",
    "title": "External Frontend Handoff",
    "description": "Share the exact Spala project contract a frontend developer or AI coding agent needs when they do not have dashboard access.",
    "section": "guides",
    "url": "https://docs.spala.ai/guides/frontend-handoff/",
    "last_updated": "2026-07-08",
    "content": "External Frontend Handoff\n\nUse this page when a React, Next.js, mobile, Webflow, or AI-built frontend needs to connect to a Spala backend but the frontend developer does not have dashboard access.\n\nThe public docs explain the stable Spala integration rules. A specific frontend still needs the generated project contract from the project owner.\n\nOwner action required\n\n  The project owner or builder should copy this packet from Lite Mode, Project Overview, Settings → API Docs, Settings → Security, Endpoints, and Channels before handing the work to an external frontend developer or AI coding agent.\n\nOpenAPI is not the whole frontend contract\n\n  OpenAPI and generated SDK files cover REST routes. They may not include realtime channel protocol, storage-provider CORS, signed-upload PUT headers, cookie/CSRF details, refresh/logout behavior, or mobile callback rules unless those details are separately documented in the project handoff.\n\nCopyable handoff packet\n\n# Spala frontend handoff\n\nProject name:\nEnvironment: preview | production\nPublished backend API: yes | no\n\nAPI_BASE_URL=\nOpenAPI JSON file or URL=\nGenerated SDK file or URL=\nMarkdown API docs file or URL=\n\nAuth:\n- Login route:\n- Signup/register route:\n- Token response field:\n- Protected request header: Authorization: Bearer USER_JWT\n- Token lifetime / refresh behavior:\n- Session mode: bearer token | refresh token | cookie session | custom\n- Cookie credential mode, if used: include | same-origin | none\n- CSRF requirement, if cookie session is used:\n- Logout behavior:\n\nRoutes:\n- List the main /api/... routes or attach the generated API Docs export.\n\nErrors:\n- HTTP status codes used by each route:\n- Error response JSON shape:\n- Validation error shape:\n- Auth error shape:\n- Request id / trace id field, if exposed:\n\nFiles/uploads:\n- Upload mode: multipart | signed URL | addon-backed storage | none\n- Upload route:\n- Max file size:\n- Returned file object shape:\n\nRealtime:\n- WebSocket URL or path:\n- Auth mode: query token | cookie | subprotocol | API key | project-specific handshake | none\n- Subscribe message envelope:\n- Event payload examples:\n- Heartbeat interval / pong timeout:\n- Close codes:\n- Replay or no replay:\n- Permissions/channel access:\n- Reconnect expectations:\n\nCORS:\n- Frontend origin:\n- Origin added in Settings → Security → CORS Allowed Origins: yes | no\n- Backend API republished after CORS/settings change: yes | no\n- Credentials required by browser requests: yes | no\n- Expected preflight headers:\n\nPublic docs:\n- Public /api/docs enabled: yes | no\n- If no, attach private Markdown/OpenAPI/SDK exports.\n\nMinimum viable handoff\n\nAt minimum, provide these four items:\n\n1. The exact API_BASE_URL copied from Lite Mode or Project Overview.\n2. The generated OpenAPI JSON, Markdown docs, or TypeScript SDK from Settings → API Docs.\n3. The auth route and token response shape.\n4. Confirmation that the frontend origin is allowed in Settings → Security → CORS Allowed Origins.\n\nWithout those, an external frontend developer can understand Spala but cannot safely wire a real private project.\n\nIf the project uses uploads, realtime, cookies, CSRF, refresh tokens, OAuth callbacks, or direct-to-storage signed URLs, the owner must also include those contracts. Do not assume OpenAPI or the generated SDK fully describes them.\n\nAuth/session fields to include\n\nGenerated Spala projects commonly use bearer JWT auth, but projects can define different session behavior. Include the exact variant used by the project:\n\n| Session field | What the frontend needs |\n| --- | --- |\n| Login and signup routes | Exact /api/... paths and request bodies from generated API Docs |\n| Token field | Exact JSON field such as token, accessToken, or data.token |\n| Refresh behavior | Whether refresh exists, the refresh route, expiry, and retry rules |\n| Cookie sessions | Whether requests need credentials: \"include\" and whether CSRF headers are required |\n| Logout | Whether logout is local token clear, server-side session revoke, or both |\n| Error semantics | Expected 401 and 403 behavior |\n\nIf the project uses cookies, include the cookie mode and CSRF header names. If it uses bearer tokens, include the token lifetime and storage expectation. Public Spala docs describe the patterns; the generated project docs are the source of truth.\n\nREST error fields to include\n\nREST error handling is route-specific. A public documentation page cannot provide a private project's actual error response bodies. Before a frontend developer or AI agent wires error handling, the project owner should copy the status codes and error JSON examples from generated API Docs, OpenAPI, SDK types, or API Playground.\n\nAt minimum, include:\n\n| Error field | What the frontend needs |\n| --- | --- |\n| Status codes | Expected 400, 401, 403, 404, 409, 422, and 500 cases for each route, as applicable |\n| Error body shape | Exact JSON body returned by the route, such as message, code, details, fieldErrors, or project-specific fields |\n| Validation errors | Field-level validation format and how arrays/nested objects are reported |\n| Auth errors | Token-expired, missing-token, invalid-token, and insufficient-permission behavior |\n| Request trace | Request id, trace id, or log correlation field if the project exposes one |\n\nDo not assume one universal error schema across all Spala projects. If a route's error response is not shown in generated docs, test the route in API Playground before wiring frontend error handling.\n\nCORS fields to include\n\nFor browser frontends, include the exact deployed frontend origin, not a wildcard or partial host:\n\nFrontend origin: https://app.example.com\nMethods used: GET, POST, PUT, PATCH, DELETE\nRequest headers: Content-Type, Authorization\nCredentials: true | false\n\nThe origin must match the browser page origin, including scheme, host, and port. After changing Settings → Security → CORS Allowed Origins, publish the backend API again before asking the frontend developer to retest.\n\nUse CORS and Security for the exact owner checklist.\n\nExample SDK handoff\n\nIf Settings → API Docs generated a TypeScript SDK, attach it or share the authenticated download route. A minimal generated SDK normally exposes the base URL, typed request helpers, and route functions similar to this:\n\nexport type SpalaClientOptions = {\n  baseUrl: string;\n  token?: string;\n};\n\nexport function createSpalaClient(options: SpalaClientOptions) {\n  async function request<T>(path: string, init: RequestInit = {}): Promise<T> {\n    const response = await fetch(`${options.baseUrl}${path}`, {\n      ...init,\n      headers: {\n        \"Content-Type\": \"application/json\",\n        ...(options.token ? { Authorization: `Bearer ${options.token}` } : {}),\n        ...init.headers,\n      },\n    });\n\n    if (!response.ok) {\n      throw new Error(`Spala API error ${response.status}`);\n    }\n\n    return response.json() as Promise<T>;\n  }\n\n  return {\n    listCases: () => request<Array<{ id: string; title: string }>>(\"/api/cases\"),\n  };\n}\n\nThis snippet shows the expected shape only. Use the project-generated sdk.ts for real route names and types.\n\nRealtime fields to include\n\nIf the project uses realtime channels, the owner should copy the contract from Channels and the generated project docs into this handoff. There is no safe public-docs default for a private project's realtime protocol.\n\nOwner workflow:\n\n1. Open the project in the dashboard.\n2. Open Channels and select the channel used by the frontend.\n3. Copy the exact WebSocket path or URL, auth mode, allowed audience, and permissions.\n4. Copy subscribe, event, error, heartbeat, close-code, replay, and reconnect examples from the channel configuration or generated project docs.\n5. Add those examples to the handoff packet before assigning frontend work.\n\nInclude a complete message sample:\n\n{\n  \"connectUrl\": \"wss://PROJECT_HOST/ws?token=USER_JWT\",\n  \"subscribe\": { \"type\": \"subscribe\", \"channel\": \"cases\" },\n  \"event\": {\n    \"type\": \"case.updated\",\n    \"channel\": \"cases\",\n    \"payload\": { \"id\": \"case_123\", \"status\": \"open\" }\n  },\n  \"error\": { \"type\": \"error\", \"code\": \"unauthorized\", \"message\": \"Invalid token\" }\n}\n\nAlso state whether replay, presence, acknowledgements, or reconnect backoff are implemented. If the project does not define those behaviors, the frontend should treat realtime as best-effort live updates and fetch REST state after reconnect.\n\nNo dashboard access workflow\n\n1. Project owner publishes or previews the backend API.\n2. Project owner exports API Docs from Settings → API Docs.\n3. Project owner copies the API base URL from Lite Mode or Project Overview.\n4. Project owner copies realtime, upload, cookie/CSRF, refresh/logout, and mobile callback details when the project uses them.\n5. Project owner adds the frontend origin in Settings → Security.\n6. Project owner republishes the backend API after CORS/security changes.\n7. Project owner sends the handoff packet and generated docs to the frontend developer.\n8. Frontend developer stores the base URL in VITE_SPALA_API_BASE_URL or NEXT_PUBLIC_SPALA_API_BASE_URL.\n9. Frontend developer tests one public route, one auth route, and one protected route before building the full UI.\n\nVerification commands\n\nReplace the placeholders with project values.\n\ncurl \"$API_BASE_URL/api/health\"\n\ncurl -X POST \"$API_BASE_URL/api/auth/login\" \\\n  -H \"Content-Type: application/json\" \\\n  --data '{\"email\":\"user@example.com\",\"password\":\"example\"}'\n\ncurl \"$API_BASE_URL/api/me\" \\\n  -H \"Authorization: Bearer USER_JWT\"\n\nIf browser calls fail but curl works, check CORS first. The frontend origin must match exactly, including scheme and port, and published settings must be live.\n\nHandoff quality checklist\n\n- API_BASE_URL is exact and includes any required scoped path.\n- OpenAPI, Markdown docs, or SDK matches the currently published API.\n- Auth token response is documented with the exact field name.\n- Route-specific error response shapes are documented or verified in API Playground.\n- Upload and realtime contracts are included when those features are used.\n- CORS origin is configured before browser integration starts.\n- Public docs are intentionally enabled, or private docs exports are attached.\n- The frontend developer knows whether they are targeting preview or production.\n\nFrontend Integration Cookbook\n/guides/frontend-integration\nStable frontend request, auth, upload, realtime, and CORS patterns\nAPI Docs\n/features/api-docs\nExport generated project contracts\nCORS and Security\n/settings/cors-security\nAdd frontend origins and publish security changes\nPublish\n/features/publish-export\nPublish backend API changes before external integrations depend on them"
  },
  {
    "path": "/guides/frontend-integration",
    "title": "Frontend Integration Cookbook",
    "description": "Connect a frontend to a Spala backend with base URL, routes, auth headers, file upload, realtime, and env vars.",
    "section": "guides",
    "url": "https://docs.spala.ai/guides/frontend-integration/",
    "last_updated": "2026-07-08",
    "content": "Frontend Integration Cookbook\n\nUse this page when a frontend developer or AI coding agent needs to connect an app UI to a Spala project API.\n\nSpecific project handoff required\n\n  Public docs cannot contain a private project's real base URL, route list, auth payloads, upload modes, or realtime channels. To integrate a specific project, start with that project's generated API Docs export from Settings → API Docs. Use this page to understand the stable Spala integration patterns around that project contract.\n\nIf the frontend developer or AI coding agent does not have dashboard access, first send the packet from External Frontend Handoff. It defines the exact API_BASE_URL, OpenAPI/SDK files, auth routes, upload mode, realtime path, and CORS origin needed for a safe integration.\n\nGuaranteed discovery flow\n\nSpala projects are configurable, so the public docs cannot safely derive a real project API base URL or route list from a slug. The guaranteed integration flow is:\n\n1. Open the project in https://dashboard.spala.ai.\n2. Copy the API base URL from Lite Mode or Project Overview.\n3. Open Settings → API Docs.\n4. Export or copy the project contract: Markdown docs, JSON docs, OpenAPI JSON, SDK files, or snippets where enabled.\n5. Wire the frontend from that generated contract.\n6. Use this cookbook for the standard request, auth, upload, realtime, environment, and CORS patterns.\n\nExample OpenAPI shape:\n\nhttps://docs.spala.ai/examples/spala-openapi-example.json\n\nThat file is an example contract shape only. Real projects expose their own generated API contract from Settings → API Docs.\n\nREST docs are not the whole integration\n\n  OpenAPI JSON and generated SDK files describe REST endpoints. They may not cover realtime channel protocol, signed-upload storage CORS, required direct-upload PUT headers, cookie/CSRF behavior, refresh/logout rules, OAuth callback URLs, or mobile-specific storage and resume behavior. Include those details separately in the frontend handoff when the project uses them.\n\nProject integration artifacts\n\nUse the generated project artifacts as the source of truth. In the hosted dashboard, Settings → API Docs gives the same contract in UI form. For authenticated project tooling, the generated docs routes are:\n\n| Artifact | Route | Access |\n| --- | --- | --- |\n| Markdown API docs | GET {{PROJECT_BASE_URL}}/api/__internal/docs | Builder authentication |\n| JSON API docs | GET {{PROJECT_BASE_URL}}/api/__internal/docs?format=json | Builder authentication |\n| OpenAPI JSON | GET {{PROJECT_BASE_URL}}/api/__internal/docs/openapi.json | Builder authentication |\n| TypeScript SDK | GET {{PROJECT_BASE_URL}}/api/__internal/docs/sdk.ts | Builder authentication |\n| Public Markdown docs | GET {{PROJECT_BASE_URL}}/api/docs | Only when public docs are enabled in Security |\n| Public JSON docs | GET {{PROJECT_BASE_URL}}/api/docs?format=json | Only when public docs are enabled in Security |\n| Swagger UI | Settings → API Docs | Use the dashboard UI unless your project explicitly exposes a public Swagger route |\n\nFor shared-runtime projects, the project base can include a scoped path such as https://shared.spala.ai/PROJECT_ID. Do not strip that path. Use the exact base URL and full URLs shown by the generated docs.\n\nDo not expose builder docs by accident\n\n  The api/__internal/docs routes require builder authentication. Only enable public api/docs when you intentionally want the project API contract to be readable without builder auth.\n\nFind the API base URL\n\nThe API base URL is shown in Lite Mode and Project Overview. Copy it from the project workspace after the project has been published or previewed.\n\nUse the full base URL plus the endpoint path:\n\nSPALA_API_BASE_URL=https://your-project-host.example\nGET {{SPALA_API_BASE_URL}}/api/cases\nPOST {{SPALA_API_BASE_URL}}/api/cases\n\nDo not guess the host. Use the value shown in the project.\n\nContract discovery matrix\n\n| Need | Where to find it | Stable rule |\n| --- | --- | --- |\n| API base URL | Lite Mode or Project Overview | Copy the displayed value; do not infer it from project name |\n| REST routes | Endpoints or Settings → API Docs | Use the exact /api/... paths in the project |\n| Markdown/JSON docs | Settings → API Docs or GET /api/__internal/docs | Use the generated project artifact as the source of truth |\n| OpenAPI JSON | Settings → API Docs or GET /api/__internal/docs/openapi.json | Use the generated project artifact as the source of truth |\n| TypeScript SDK | Settings → API Docs or GET /api/__internal/docs/sdk.ts | Use the generated project artifact as the source of truth |\n| Auth routes | Endpoints or API Docs | Common routes are /api/auth/login and /api/auth/register, but project docs are authoritative |\n| Upload mode | API Docs and file upload endpoint | Multipart and signed URL flows are different contracts |\n| Realtime URL | Channels and generated project docs | Use the project WebSocket path, auth mode, message envelope, heartbeat, replay, and permissions |\n| Browser origins | Settings → Security → CORS Allowed Origins | Add the frontend origin and republish if browser calls are blocked |\n\nEnvironment variables\n\nFor Vite:\n\nVITE_SPALA_API_BASE_URL=https://your-project-host.example\n\nexport const SPALA_API_BASE_URL = import.meta.env.VITE_SPALA_API_BASE_URL;\n\nFor Next.js:\n\nNEXT_PUBLIC_SPALA_API_BASE_URL=https://your-project-host.example\n\nexport const SPALA_API_BASE_URL = process.env.NEXT_PUBLIC_SPALA_API_BASE_URL!;\n\nPublic endpoint call\n\nconst response = await fetch(`${SPALA_API_BASE_URL}/api/cases`);\n\nif (!response.ok) {\n  throw new Error(`Spala API error: ${response.status}`);\n}\n\nconst cases = await response.json();\n\nProtected endpoint call\n\nProtected endpoints expect a user JWT from your login flow.\n\nconst response = await fetch(`${SPALA_API_BASE_URL}/api/me`, {\n  headers: {\n    Authorization: `Bearer ${userJwt}`,\n  },\n});\n\nif (response.status === 401) {\n  // Send the user back through your login flow.\n}\n\nconst profile = await response.json();\n\nAuth contract\n\nCommon generated auth endpoints return a user token that frontends send as a bearer token. Treat the project API Docs as the authoritative contract for exact route names and response shape.\n\nTypical login response:\n\n{\n  \"token\": \"USER_JWT\"\n}\n\nSome projects also return a user object or a data wrapper. The generated API Docs for the project are authoritative; the stable frontend rule is to extract the token value returned by the login endpoint and send it as a bearer token.\n\nTypical protected request:\n\nAuthorization: Bearer USER_JWT\n\nRecommended frontend behavior:\n\n- Keep tokens out of logs and source control.\n- On 401, clear local session state and send the user through login again.\n- On 403, keep the user logged in but show a permissions message.\n- Confirm refresh-token behavior from the generated project API Docs if the project implements refresh tokens.\n\nSession variants\n\n| Variant | Frontend request behavior | Notes |\n| --- | --- | --- |\n| Bearer JWT | Send Authorization: Bearer USER_JWT on protected requests | Most common generated pattern |\n| Bearer JWT with refresh | Retry once through the project refresh route, then send the new token | Use only if generated API Docs expose refresh |\n| Cookie session | Send credentials: \"include\" and follow CSRF rules if configured | Requires exact CORS credentials settings |\n| API key or custom header | Send the header documented by the project | Common for server-to-server or internal tools |\n\nBrowser clients cannot invent session behavior. If the project docs do not mention refresh tokens, cookie sessions, or CSRF, assume bearer-token login and re-login on expiry.\n\nToken storage\n\n  Store user tokens according to your frontend threat model. Prefer short-lived tokens, avoid logging tokens, and never commit real tokens into source code or docs.\n\nLogin example\n\nThe exact route names depend on your project. A common pattern is:\n\nconst response = await fetch(`${SPALA_API_BASE_URL}/api/auth/login`, {\n  method: \"POST\",\n  headers: {\n    \"Content-Type\": \"application/json\",\n  },\n  body: JSON.stringify({\n    email,\n    password,\n  }),\n});\n\nif (!response.ok) {\n  throw new Error(\"Login failed\");\n}\n\nconst { token } = await response.json();\n\nFile upload example\n\nUse FormData for multipart upload endpoints.\n\nconst form = new FormData();\nform.append(\"file\", file);\n\nconst response = await fetch(`${SPALA_API_BASE_URL}/api/upload`, {\n  method: \"POST\",\n  headers: {\n    Authorization: `Bearer ${userJwt}`,\n  },\n  body: form,\n});\n\nif (!response.ok) {\n  throw new Error(`Upload failed: ${response.status}`);\n}\n\nconst uploadedFile = await response.json();\n\nIf your project uses S3 or signed URLs, follow the route contract generated in Settings → API Docs. Do not mix multipart upload and signed URL flows unless the project explicitly exposes both.\n\nUpload contract matrix\n\n| Upload mode | Request | Response | When to use |\n| --- | --- | --- | --- |\n| Multipart endpoint | POST /api/upload with FormData | JSON record for the stored file | Simple browser file uploads through the project API |\n| Signed URL | Request signed URL, then upload directly to storage | Storage URL or file metadata | Larger files or direct-to-S3 style flows |\n| Addon-backed storage | Project-specific endpoint calls an addon such as S3 | Project-defined JSON | When storage provider behavior is part of backend logic |\n\nSigned URL frontend sequence:\n\nconst signResponse = await fetch(`${SPALA_API_BASE_URL}/api/uploads/sign`, {\n  method: \"POST\",\n  headers: {\n    \"Content-Type\": \"application/json\",\n    Authorization: `Bearer ${userJwt}`,\n  },\n  body: JSON.stringify({\n    filename: file.name,\n    contentType: file.type,\n    size: file.size,\n  }),\n});\n\nconst { uploadUrl, fileId, headers = {} } = await signResponse.json();\n\nconst uploadResponse = await fetch(uploadUrl, {\n  method: \"PUT\",\n  headers,\n  body: file,\n});\n\nif (!uploadResponse.ok) {\n  throw new Error(`Storage upload failed: ${uploadResponse.status}`);\n}\n\nawait fetch(`${SPALA_API_BASE_URL}/api/uploads/${fileId}/complete`, {\n  method: \"POST\",\n  headers: {\n    Authorization: `Bearer ${userJwt}`,\n  },\n});\n\nFor signed URLs, the storage bucket CORS policy may also need to allow the frontend origin. That is separate from the Spala project API CORS setting.\n\nSigned upload contract fields to include:\n\n| Field | Why it matters |\n| --- | --- |\n| Required PUT headers | Some storage signatures require exact Content-Type, checksum, or metadata headers |\n| Content type | The uploaded Content-Type may need to match the signing input exactly |\n| Size limits | The project or storage provider may reject files above the signed limit |\n| Retry/cancel behavior | Retrying may require a new signed URL if the old one expires |\n| Complete body | The finalize route may need storage key, file id, checksum, or public URL |\n| File object schema | Frontends need the returned id, url, name, size, and metadata shape |\n\nCORS and browser request behavior\n\nServer-to-server calls and curl do not prove that browser calls will work. Browser frontends also need a matching CORS configuration.\n\nChecklist for browser calls:\n\n1. Add the exact frontend origin in Settings → Security → CORS Allowed Origins.\n2. Include the scheme and port, such as http://localhost:5173 or https://app.example.com.\n3. Publish the backend API after changing CORS settings.\n4. If the frontend sends bearer tokens, allow the Authorization request header.\n5. If the frontend sends JSON bodies, allow Content-Type.\n6. If the project uses cookie sessions, enable credentialed requests and call fetch with credentials: \"include\".\n\nUse CORS and Security for the owner-side settings checklist.\n\nExample preflight the browser may send:\n\nOPTIONS /api/cases HTTP/1.1\nOrigin: https://app.example.com\nAccess-Control-Request-Method: POST\nAccess-Control-Request-Headers: authorization, content-type\n\nExpected allowed response shape:\n\nAccess-Control-Allow-Origin: https://app.example.com\nAccess-Control-Allow-Methods: GET, POST, PUT, PATCH"
  },
  {
    "path": "/guides/scheduled-jobs",
    "title": "Run Scheduled Jobs",
    "description": "Automate cleanup routines, reports, and data syncing with Spala tasks.",
    "section": "guides",
    "url": "https://docs.spala.ai/guides/scheduled-jobs/",
    "last_updated": "2026-07-08",
    "content": "Run Scheduled Jobs\n\nUse tasks for backend work that should run without a user request: cleanup routines, recurring reports, imports, sync jobs, and long-running operations.\n\nSteps\n\nCreate a task\n\n    Go to Tasks and click New Task. Give it a clear name, such as cleanup-expired-sessions or sync-stripe-customers.\n\nChoose how it runs\n\n    Set a repeat frequency and unit, such as every 15 minutes, every 1 hour, or every 1 day. Leave the schedule empty when you want a manual task that only runs when started directly.\n\nBuild the task function\n\n    Add steps for the work the task should do. For a cleanup task, query stale records, delete or update them, then log the result.\n\nTune execution\n\n    Use Run in Background for longer work. Set timeout and memory limits so expensive jobs fail predictably instead of silently consuming resources.\n\nExample Cleanup Task\n\n1. Set Variable - Name: cutoff, Value: '' | now | addHours(-24) | toISO\n2. Database Delete - Table: sessions, Where: { expires_at: { $lt: vars.cutoff } }, Returning: id, Assign To: deleted\n3. Log - Message: 'Cleaned up ' + (vars.deleted | length) + ' expired sessions'\n\nWhen To Use Manual Tasks\n\nManual tasks are useful for admin jobs that should not run on a timer:\n\n- Backfill missing data after an import\n- Recalculate analytics\n- Re-send a batch of notifications\n- Run a one-off cleanup before publish\n\nUse background execution for long jobs\n\n  Tasks that process many records or call external APIs should run in the background. Monitor them from Background Tasks to see status, logs, result data, retries, and failures.\n\nScheduled Tasks\n/tasks/scheduled-tasks\nConfigure repeat frequency, manual runs, and resource limits\nBackground Tasks\n/tasks/background-tasks\nMonitor queued, running, completed, and failed work\nSend Emails\n/guides/send-emails\nSend reports and alerts from tasks"
  },
  {
    "path": "/guides/send-emails",
    "title": "Send Emails",
    "description": "Configure email delivery and send transactional emails from your backend functions.",
    "section": "guides",
    "url": "https://docs.spala.ai/guides/send-emails/",
    "last_updated": "2026-07-08",
    "content": "Send Emails\n\nSet up email delivery and send transactional emails — password resets, welcome messages, order confirmations — from any backend function.\n\nWhat you will build\n\n- Email addon configured with SMTP or SendGrid\n- POST /api/auth/forgot-password — sends a password reset email\n- A reusable email template pattern\n\nSteps\n\nInstall the email addon\n\n    Go to Addons and install the Email addon. Then add your provider's credentials in Settings > Environment Variables:\n\n    For SMTP:\n    - SMTP_HOST — e.g., smtp.gmail.com\n    - SMTP_PORT — typically 587\n    - SMTP_USER — your email address\n    - SMTP_PASS — your password or app password\n\n    For SendGrid:\n    - SENDGRID_API_KEY — your API key from the SendGrid dashboard\n\nBuild the forgot-password endpoint\n\n    Create POST /api/auth/forgot-password:\n\n    1. Find One — Table: users, Where: { email: input.email }, Assign To: user\n    2. If/Else — Condition: vars.user == null\n       - True: Respond — Status: 200, Body: { message: 'If an account exists, a reset email has been sent' }\n    3. Random String — Length: 32, Characters: hex, Assign To: resetToken\n    4. Database Update — Table: users, Data: { reset_token: vars.resetToken, reset_expires: '' | now | addHours(1) | toISO }, Where: { id: vars.user.id }\n    5. Send Email — To: vars.user.email, Subject: 'Password Reset Request', Body: your HTML template (see next step)\n    6. Respond — Status: 200, Body: { message: 'If an account exists, a reset email has been sent' }\n\nCreate the email template\n\n    Use HTML in the Send Email body for formatted emails:\n\n    '<h1>Reset Your Password</h1>' +\n    '<p>Hi ' + vars.user.name + ',</p>' +\n    '<p>Click the link below to reset your password. This link expires in 1 hour.</p>' +\n    '<a href=\"' + env.APP_URL + '/reset-password?token=' + vars.resetToken + '\">Reset Password</a>'\n\nTest email delivery\n\n    Use the Playground to call your forgot-password endpoint. During development, use a service like Mailtrap to capture test emails without sending real ones.\n\nPro tip\n\n  For production, use a dedicated email service like SendGrid, Mailgun, or Amazon SES. They offer better deliverability and higher sending limits than personal SMTP.\n\nAdd User Authentication\n/guides/user-auth\nSet up the user system that needs emails\nAddons\n/addons\nBrowse email and communication addons"
  },
  {
    "path": "/guides/file-uploads",
    "title": "Upload Files",
    "description": "Accept file uploads, store them in S3 or locally, and serve them back.",
    "section": "guides",
    "url": "https://docs.spala.ai/guides/file-uploads/",
    "last_updated": "2026-07-08",
    "content": "Upload Files\n\nAccept file uploads from clients, validate them, store them in S3 or locally, and serve them back through your API.\n\nWhat you will build\n\n- POST /api/upload — accept and store a file\n- GET /api/files/:filename — serve a stored file\n\nSteps\n\nInstall a storage addon\n\n    Go to Addons and install Local Storage (for development) or S3 Storage (for production). S3 requires your bucket name, region, access key, and secret key in Settings > Environment Variables.\n\nCreate a files table\n\n    In the Database screen, create a files table:\n\n    | Column | Type | Notes |\n    |--------|------|-------|\n    | id | Serial | Primary key |\n    | filename | Text | Original file name |\n    | path | Text | Storage path |\n    | size | Integer | File size in bytes |\n    | mimetype | Text | e.g., image/png |\n    | created_at | Timestamp | Default: now() |\n\nBuild the upload endpoint\n\n    Create POST /api/upload:\n\n    1. Upload File — Field Name: file, Destination: 'uploads/', Max Size: 5242880 (5 MB), Allowed Types: ['image/png', 'image/jpeg', 'application/pdf'], Assign To: uploaded\n    2. Database Insert — Table: files, Data: { filename: vars.uploaded.originalName, path: vars.uploaded.path, size: vars.uploaded.size, mimetype: vars.uploaded.mimetype }, Returning: *, Assign To: fileRecord\n    3. Respond — Status: 201, Body: { data: vars.fileRecord }\n\nBuild the download endpoint\n\n    Create GET /api/files/:filename:\n\n    1. Find One — Table: files, Where: { filename: params.filename }, Assign To: fileRecord\n    2. If/Else — Condition: vars.fileRecord == null\n       - True: Respond — Status: 404, Body: { error: 'File not found' }\n    3. Download File — Path: vars.fileRecord.path, Filename: vars.fileRecord.filename, Content Type: vars.fileRecord.mimetype\n\nTest the upload\n\n    In the Playground, select POST /api/upload. Switch the body type to Form Data, add a field named file with type File, pick a file from your computer, and click Send.\n\nGood to know\n\n  When you switch from Local Storage to S3 Storage for production, your upload and download flows stay the same. The storage addon handles the difference.\n\nSigned URL Upload Flow\n\nSome projects avoid sending large files through the project API. In that setup, the frontend asks Spala for a signed storage URL, uploads the file directly to storage, then calls the project API again to finalize metadata.\n\nTypical frontend sequence:\n\n1. POST /api/uploads/sign with filename, content type, and size.\n2. Receive uploadUrl, optional required headers, and a project file id.\n3. PUT the file bytes directly to uploadUrl.\n4. POST /api/uploads/:id/complete so the backend records metadata and returns the project file object.\n\nExample:\n\nconst signed = await fetch(`${SPALA_API_BASE_URL}/api/uploads/sign`, {\n  method: \"POST\",\n  headers: {\n    \"Content-Type\": \"application/json\",\n    Authorization: `Bearer ${userJwt}`,\n  },\n  body: JSON.stringify({\n    filename: file.name,\n    contentType: file.type,\n    size: file.size,\n  }),\n}).then((response) => response.json());\n\nawait fetch(signed.uploadUrl, {\n  method: \"PUT\",\n  headers: signed.headers ?? {},\n  body: file,\n});\n\nconst fileRecord = await fetch(`${SPALA_API_BASE_URL}/api/uploads/${signed.fileId}/complete`, {\n  method: \"POST\",\n  headers: {\n    Authorization: `Bearer ${userJwt}`,\n  },\n}).then((response) => response.json());\n\nThe exact route names and response fields are generated by the project. If the upload goes directly to S3 or another storage provider, that storage provider also needs its own CORS policy for the frontend origin.\n\nAddons\n/addons\nBrowse storage addons\nBuild a REST API\n/guides/build-rest-api\nCreate the API your uploads belong to"
  },
  {
    "path": "/reference/steps/api",
    "title": "API Steps",
    "description": "Steps for making HTTP requests, sending responses, and managing headers and cookies.",
    "section": "reference",
    "url": "https://docs.spala.ai/reference/steps/api/",
    "last_updated": "2026-07-08",
    "content": "API Steps\n\nAPI steps handle HTTP communication -- making outbound requests to external services and sending responses back to clients.\n\nExternal API Call\n\nMakes an HTTP request to an external API. Supports all common HTTP methods and authentication schemes.\n\nMethod: POST\nURL: 'https://api.stripe.com/v1/charges'\nHeaders: { 'Content-Type': 'application/x-www-form-urlencoded' }\nBody: { amount: vars.amount, currency: 'usd' }\nAuth Type: bearer\nAuth Value: env.STRIPE_SECRET_KEY\nAssign To: chargeResult\n\nThe result stored in vars.chargeResult contains:\n- body -- the parsed response body\n- status -- the HTTP status code\n- headers -- the response headers\n\nRespond\n\nSends an HTTP response to the client. Use this to control exactly what the client receives.\n\nStatus Code: 201\nBody: { success: true, user: vars.newUser }\nHeaders: { 'X-Request-Id': vars.requestId }\n\n  If no Respond step is used, the flow automatically responds with the value from the Return step, or an empty 200 response.\n\nRedirect\n\nSends an HTTP redirect response to the client.\n\nURL: '/dashboard'\nStatus Code: 302\n\nSet Header\n\nSets a response header for the current request.\n\nName: Content-Type\nValue: 'application/xml'\n\nSet Cookie\n\nSets a cookie on the client response.\n\nName: session_token\nValue: vars.token\nMax Age: 86400\nHTTP Only: true\nSecure: true\nSame Site: strict\n\nHTTP method: `GET`, `POST`, `PUT`, `PATCH`, `DELETE`, `HEAD`, `OPTIONS`.\nThe full URL to call.\nRequest headers as key-value pairs.\nRequest input. Automatically serialized to JSON for object values.\nURL query parameters as key-value pairs.\nAuthentication type: `none`, `bearer`, `basic`, `api-key`.\nThe token, credentials, or API key depending on auth type.\nRequest timeout in milliseconds. Defaults to 30000.\nVariable name to store the response (includes `body`, `status`, `headers`).\nHTTP status code. Defaults to 200.\nThe response input.\nAdditional response headers.\nThe URL to redirect to.\nRedirect status code: `301` (permanent) or `302` (temporary). Defaults to 302.\nThe header name.\nThe header value.\nThe cookie name.\nThe cookie value.\nCookie lifetime in seconds.\nCookie path. Defaults to `/`.\nWhether the cookie is inaccessible to JavaScript. Defaults to true.\nWhether to only send over HTTPS. Defaults to false.\nSameSite policy: `strict`, `lax`, or `none`.\nSteps Reference\n/reference/steps\nBrowse all step categories\nAPI Endpoints\n/endpoints\nCreate endpoints that use API steps\nAuth Steps\n/reference/steps/auth\nAdd authentication to your API"
  },
  {
    "path": "/reference/filters/array",
    "title": "Array Filters",
    "description": "Filters for transforming, searching, and manipulating arrays.",
    "section": "reference",
    "url": "https://docs.spala.ai/reference/filters/array/",
    "last_updated": "2026-07-08",
    "content": "Array Filters\n\nArray filters manipulate lists of data. They are especially useful with database results and collections.\n\nUsage examples\n\nGet unique categories from products:\n\nvars.products | map('category') | unique | sort\n\nPaginate results:\n\nvars.allItems | slice(vars.offset, vars.offset + vars.limit)\n\nCalculate order total:\n\nvars.orderItems | reduce('price', 0)\n\nGroup and count by status:\n\nvars.orders | groupBy('status')\n// { pending: [...], shipped: [...], delivered: [...] }\n\nFilters Reference\n/reference/filters\nBrowse all filter categories\nObject Filters\n/reference/filters/object\nTransform objects and key-value pairs\nString Filters\n/reference/filters/string\nTransform text strings"
  },
  {
    "path": "/reference/steps/auth",
    "title": "Auth Steps",
    "description": "Steps for JWT tokens, password hashing, and API key generation.",
    "section": "reference",
    "url": "https://docs.spala.ai/reference/steps/auth/",
    "last_updated": "2026-07-08",
    "content": "Auth Steps\n\nAuth steps provide common authentication and authorization operations -- creating tokens, hashing passwords, and generating API keys.\n\nSign JWT\n\nCreates a JSON Web Token with a payload and signing secret.\n\nPayload: { userId: vars.user.id, role: vars.user.role }\nSecret: env.JWT_SECRET\nExpires In: 7d\nAssign To: token\n\nVerify JWT\n\nVerifies a JWT and extracts its payload. Throws an error if the token is invalid or expired.\n\nToken: headers.authorization | split(' ') | last\nSecret: env.JWT_SECRET\nAssign To: decoded\n\n  Wrap Verify JWT in a Try/Catch step to handle invalid or expired tokens gracefully and return a 401 response.\n\nHash Password\n\nHashes a plaintext password using bcrypt for secure storage.\n\nPassword: input.password\nSalt Rounds: 12\nAssign To: hashedPassword\n\nVerify Password\n\nCompares a plaintext password against a bcrypt hash. Returns true if they match, false otherwise.\n\nPassword: input.password\nHash: vars.user.password_digest\nAssign To: isValid\n\nGenerate API Key\n\nGenerates a cryptographically secure random API key string.\n\nPrefix: sk_live_\nLength: 48\nAssign To: apiKey\n\nThe data to encode in the token (e.g., user ID, role).\nThe signing secret. Use `env.JWT_SECRET` for environment variables.\nToken expiration duration (e.g., `1h`, `7d`, `30m`). Defaults to `1h`.\nSigning algorithm: `HS256`, `HS384`, `HS512`. Defaults to `HS256`.\nVariable name to store the generated token string.\nThe JWT string to verify.\nThe signing secret used to create the token.\nVariable name to store the decoded payload.\nThe plaintext password to hash.\nNumber of bcrypt salt rounds. Defaults to 10.\nVariable name to store the hashed password.\nThe plaintext password to verify.\nThe bcrypt hash to compare against.\nVariable name to store the boolean result.\nLength of the generated key in characters. Defaults to 32.\nOptional prefix for the key (e.g., `sk_live_`).\nVariable name to store the generated key.\nSteps Reference\n/reference/steps\nBrowse all step categories\nAdd User Authentication\n/guides/user-auth\nStep-by-step auth guide\nCrypto Steps\n/reference/steps/crypto\nEncryption and hashing steps"
  },
  {
    "path": "/reference/steps/control-flow",
    "title": "Control Flow Steps",
    "description": "Steps for conditional branching, loops, error handling, and flow control.",
    "section": "reference",
    "url": "https://docs.spala.ai/reference/steps/control-flow/",
    "last_updated": "2026-07-08",
    "content": "Control Flow Steps\n\nControl flow steps determine the execution path of your flow -- branching, looping, and handling errors.\n\nIf/Else\n\nExecutes one branch if a condition is true, and optionally another if it is false.\n\nThe If/Else step creates two branches on the canvas: a True branch and a False branch. Place subsequent steps inside either branch.\n\nCondition: vars.user != null && vars.user.role == 'admin'\n\nSwitch\n\nMulti-way branching based on the value of an expression. Each case defines a value to match and a branch of steps to execute.\n\nExpression: params.action\nCases: ['create', 'update', 'delete']\nDefault: true\n\nLoop\n\nIterates over an array or a numeric range, executing the contained steps for each iteration.\n\nIterable: vars.products\nItem Variable: product\nIndex Variable: i\n\nInside the loop body, access vars.product and vars.i.\n\nFor Each\n\nA simplified loop that iterates over an array. Functionally similar to Loop but optimized for common array iteration.\n\nArray: vars.orderItems\nItem Variable: orderItem\n\nWhile\n\nRepeats the contained steps as long as a condition remains true.\n\nCondition: vars.retryCount < 3 && vars.success == false\n\n  Be careful with While loops. If the condition never becomes false, the flow will run indefinitely. Always include logic inside the loop that eventually changes the condition.\n\nTry/Catch\n\nWraps steps in error handling. If any step in the Try block throws an error, execution jumps to the Catch block.\n\nThe Try/Catch step creates two branches: a Try branch for the main logic and a Catch branch for error handling.\n\nError Variable: err\n\nInside the Catch branch, access vars.err.message and vars.err.stack.\n\nBreak\n\nExits the current loop immediately. Only valid inside Loop, For Each, or While steps.\n\nBreak has no configurable properties. Drag it into a loop body and it will stop iteration when reached.\n\nContinue\n\nSkips the remainder of the current loop iteration and proceeds to the next one. Only valid inside Loop, For Each, or While steps.\n\nContinue has no configurable properties. Place it inside a conditional branch within a loop to skip specific iterations.\n\nThrow Error\n\nThrows an error with a custom message. If inside a Try/Catch block, the error is caught. Otherwise, the flow terminates with an error response.\n\nMessage: 'User not found'\nStatus Code: 404\n\nA boolean expression to evaluate.\nThe value to match against each case.\nList of case values. Each case creates a branch on the canvas.\nWhether to include a default branch for unmatched values.\nAn array expression or range to iterate over.\nVariable name for the current item. Defaults to `item`.\nVariable name for the current index. Defaults to `index`.\nThe array to iterate over.\nVariable name for the current item.\nA boolean expression evaluated before each iteration.\nVariable name to store the caught error object. Defaults to `error`.\nThe error message.\nHTTP status code to return if the error is unhandled. Defaults to 500.\nSteps Reference\n/reference/steps\nBrowse all step categories\nVariable Steps\n/reference/steps/variables\nSet variables and return values\nOperators\n/reference/operators\nOperators for conditions and expressions"
  },
  {
    "path": "/reference/steps/crypto",
    "title": "Crypto Steps",
    "description": "Steps for encryption, decryption, hashing, and random value generation.",
    "section": "reference",
    "url": "https://docs.spala.ai/reference/steps/crypto/",
    "last_updated": "2026-07-08",
    "content": "Crypto Steps\n\nCrypto steps provide cryptographic operations -- encrypting and decrypting data, generating hashes, and producing random values.\n\nEncrypt\n\nEncrypts data using a symmetric encryption algorithm.\n\nData: vars.sensitiveData\nKey: env.ENCRYPTION_KEY\nAlgorithm: aes-256-gcm\nAssign To: encrypted\n\nDecrypt\n\nDecrypts data that was encrypted with the Encrypt step.\n\nData: vars.encrypted\nKey: env.ENCRYPTION_KEY\nAlgorithm: aes-256-gcm\nAssign To: decrypted\n\nHash\n\nGenerates a hash digest of the input data. Hashing is one-way -- the original data cannot be recovered.\n\nData: vars.fileContent\nAlgorithm: sha256\nEncoding: hex\nAssign To: checksum\n\n  For password hashing, use the Hash Password step (bcrypt) instead of this step. General-purpose hash algorithms like SHA-256 are not suitable for password storage.\n\nGenerate UUID\n\nGenerates a universally unique identifier (UUID v4).\n\nAssign To: orderId\n\nResult example: \"f47ac10b-58cc-4372-a567-0e02b2c3d479\"\n\nRandom Number\n\nGenerates a random number within a specified range.\n\nMin: 1000\nMax: 9999\nInteger: true\nAssign To: verificationCode\n\nRandom String\n\nGenerates a random string of specified length and character set.\n\nLength: 24\nCharacters: alphanumeric\nAssign To: sessionId\n\nThe data to encrypt. Strings and objects are accepted.\nThe encryption key. Use `env.ENCRYPTION_KEY` for environment variables.\nEncryption algorithm: `aes-256-cbc`, `aes-256-gcm`. Defaults to `aes-256-cbc`.\nVariable name to store the encrypted string.\nThe encrypted data string.\nThe same key used for encryption.\nMust match the algorithm used for encryption.\nVariable name to store the decrypted data.\nThe data to hash.\nHash algorithm: `md5`, `sha1`, `sha256`, `sha512`. Defaults to `sha256`.\nOutput encoding: `hex`, `base64`. Defaults to `hex`.\nVariable name to store the hash string.\nVariable name to store the UUID string.\nMinimum value (inclusive). Defaults to 0.\nMaximum value (inclusive). Defaults to 100.\nWhether to return an integer. Defaults to true.\nVariable name to store the generated number.\nLength of the string. Defaults to 16.\nCharacter set: `alphanumeric`, `alpha`, `numeric`, `hex`. Defaults to `alphanumeric`.\nVariable name to store the generated string.\nSteps Reference\n/reference/steps\nBrowse all step categories\nAuth Steps\n/reference/steps/auth\nJWT tokens and password hashing\nEncoding Steps\n/reference/steps/encoding\nBase64 and URL encoding"
  },
  {
    "path": "/reference/data-types",
    "title": "Data Types",
    "description": "Reference for all data types supported in Spala expressions and flows.",
    "section": "reference",
    "url": "https://docs.spala.ai/reference/data-types/",
    "last_updated": "2026-07-08",
    "content": "Data Types\n\nSpala supports the following data types in expressions, variables, and step properties.\n\nString\n\nA sequence of characters, enclosed in single quotes in expressions.\n\n'Hello, world!'\n'Order #' + vars.orderId\n\nStrings support all string filters for transformation.\n\nNumber\n\nInteger or floating-point numeric values. No quotes needed.\n\n42\n3.14\n-100\n\nArithmetic operators and number filters work with numeric values.\n\nBoolean\n\nA true or false value. Used in conditions, If/Else steps, and logical expressions.\n\ntrue\nfalse\nvars.isActive == true\n\nNull\n\nRepresents the intentional absence of a value. Different from undefined (which means a variable was never set).\n\nnull\n\nUse | isNull or == null to check for null values. The nullish coalescing operator ?? provides fallbacks for null values.\n\nArray\n\nAn ordered list of values. Arrays can contain any mix of data types.\n\n[1, 2, 3]\n['apple', 'banana', 'cherry']\n[{ id: 1, name: 'Alice' }, { id: 2, name: 'Bob' }]\n\nArrays are the primary way to work with collections of data. Database query results return arrays of objects. See array filters for transformation operations.\n\nObject\n\nA collection of key-value pairs. Keys are strings, values can be any type.\n\n{ name: 'Alice', age: 30, active: true }\n{ user: vars.currentUser, timestamp: '' | now }\n\nAccess properties with dot notation (vars.user.name) or bracket notation (vars.user['name']). See object filters for manipulation operations.\n\nDate\n\nDate values represent a point in time. They are created from ISO strings or the now filter.\n\n'' | now\n'2026-03-06T12:00:00Z' | fromISO\n\nDates stored in the database are automatically parsed. Use date filters to format, compare, and perform arithmetic on dates.\n\nBuffer\n\nBinary data used for file contents, encryption results, and raw data. Buffers are typically produced by file operations or crypto steps rather than created directly in expressions.\n\nBuffers can be converted to strings with an encoding:\n\nvars.fileBuffer | toString\nvars.binaryData | toBase64\n\n  All data types are automatically serialized to JSON when used in API responses. Dates become ISO strings, buffers become Base64 strings, and all other types map directly to their JSON equivalents.\n\nType checking\n\nUse the typeOf filter to inspect a value's type at runtime:\n\nvars.input | typeOf\n// Returns: 'string', 'number', 'boolean', 'object', 'array', or 'null'\n\nUse type conversion filters to ensure values are the expected type:\n\n| Filter | Description |\n|--------|-------------|\n| toString | Convert to string |\n| toNumber | Convert to number |\n| toBoolean | Convert to boolean |\n| toArray | Wrap in array if not already |\n\nSee the type filters reference for details.\n\nFilters Reference\n/reference/filters\nTransform data types with pipe filters\nOperators\n/reference/operators\nArithmetic, comparison, and logical operators"
  },
  {
    "path": "/reference/steps/database",
    "title": "Database Steps",
    "description": "Steps for querying, inserting, updating, deleting, and aggregating database records.",
    "section": "reference",
    "url": "https://docs.spala.ai/reference/steps/database/",
    "last_updated": "2026-07-08",
    "content": "Database Steps\n\nDatabase steps interact with your project's PostgreSQL database. They provide both raw SQL access and structured operations that generate SQL for you.\n\nDatabase Query\n\nExecutes a raw SQL query. Use this for complex queries that the structured steps cannot express.\n\nQuery: SELECT * FROM products WHERE price > $1 AND category = $2\nParameters: [29.99, 'electronics']\nAssign To: products\n\n  Always use parameterized queries instead of string concatenation to prevent SQL injection.\n\nDatabase Insert\n\nInserts one or more rows into a table.\n\nTable: users\nData: { name: input.name, email: input.email }\nReturning: *\nAssign To: newUser\n\nDatabase Update\n\nUpdates rows in a table matching the given conditions.\n\nTable: users\nData: { name: input.name }\nWhere: { id: vars.userId }\nReturning: *\nAssign To: updatedUser\n\nDatabase Delete\n\nDeletes rows from a table matching the given conditions.\n\nTable: sessions\nWhere: { expires_at: { $lt: 'NOW()' } }\n\nDatabase Select\n\nRetrieves rows from a table with structured filtering, sorting, and pagination.\n\nTable: products\nColumns: id, name, price\nWhere: { category: 'electronics' }\nOrder By: price ASC\nLimit: 20\nOffset: 0\nAssign To: products\n\nFind One\n\nRetrieves a single row matching the conditions. Returns null if no match is found.\n\nTable: users\nWhere: { email: input.email }\nAssign To: user\n\nFind Many\n\nRetrieves all rows matching the conditions.\n\nTable: orders\nWhere: { user_id: vars.userId, status: 'pending' }\nAssign To: pendingOrders\n\nCount\n\nReturns the number of rows matching the conditions.\n\nTable: orders\nWhere: { status: 'completed' }\nAssign To: completedCount\n\nAggregate\n\nPerforms aggregation operations on a table (SUM, AVG, MIN, MAX, COUNT).\n\nFunction: SUM\nColumn: amount\nTable: orders\nWhere: { status: 'completed' }\nGroup By: category\nAssign To: salesByCategory\n\nThe SQL query to execute. Use `$1`, `$2`, etc. for parameterized values.\nArray of values for parameterized placeholders.\nVariable name to store the query result.\nThe target table name.\nAn object (single row) or array of objects (multiple rows) to insert.\nColumns to return from the inserted rows (e.g., `*` or `id`).\nVariable name to store the returned data.\nThe target table name.\nKey-value pairs of columns to update.\nConditions to match rows for updating.\nColumns to return from the updated rows.\nVariable name to store the returned data.\nThe target table name.\nConditions to match rows for deletion.\nColumns to return from the deleted rows.\nVariable name to store the returned data.\nThe table to query.\nColumns to select. Defaults to `*`.\nFilter conditions.\nColumn and direction for sorting (e.g., `created_at DESC`).\nMaximum number of rows to return.\nNumber of rows to skip.\nVariable name to store the result.\nThe table to query.\nFilter conditions.\nVariable name to store the result.\nThe table to query.\nFilter conditions.\nVariable name to store the result array.\nThe table to query.\nFilter conditions.\nVariable name to store the count.\nThe table to query.\nThe aggregation function: `SUM`, `AVG`, `MIN`, `MAX`, or `COUNT`.\nThe column to aggregate.\nFilter conditions.\nColumn to group results by.\nVariable name to store the result.\nSteps Reference\n/reference/steps\nBrowse all step categories\nDatabase Overview\n/database\nDesign tables and relationships\nVariable Steps\n/reference/steps/variables\nStore query results in variables"
  },
  {
    "path": "/reference/filters/date",
    "title": "Date Filters",
    "description": "Filters for formatting, comparing, and manipulating dates.",
    "section": "reference",
    "url": "https://docs.spala.ai/reference/filters/date/",
    "last_updated": "2026-07-08",
    "content": "Date Filters\n\nDate filters format, compare, and perform arithmetic on date values.\n\nFormat patterns\n\nThe format filter accepts the following tokens:\n\n| Token | Output | Example |\n|-------|--------|---------|\n| YYYY | 4-digit year | 2026 |\n| MM | 2-digit month | 03 |\n| DD | 2-digit day | 06 |\n| HH | 24-hour hour | 14 |\n| mm | Minutes | 30 |\n| ss | Seconds | 45 |\n\nUsage examples\n\nFormat a creation date for display:\n\nvars.user.created_at | format('YYYY-MM-DD HH:mm')\n\nCheck if a token has expired:\n\nvars.token.expires_at | isBefore('' | now)\n\nCalculate days until deadline:\n\nvars.deadline | diffInDays('' | now)\n\nSet an expiration date 30 days from now:\n\n'' | now | addDays(30) | toISO\n\nFilters Reference\n/reference/filters\nBrowse all filter categories\nNumber Filters\n/reference/filters/number\nRound, clamp, and transform numbers\nString Filters\n/reference/filters/string\nFormat dates as strings"
  },
  {
    "path": "/reference/steps/encoding",
    "title": "Encoding Steps",
    "description": "Steps for parsing and encoding JSON, Base64, URLs, and HTML entities.",
    "section": "reference",
    "url": "https://docs.spala.ai/reference/steps/encoding/",
    "last_updated": "2026-07-08",
    "content": "Encoding Steps\n\nEncoding steps convert data between formats -- parsing JSON strings into objects, encoding data to Base64, and escaping URLs and HTML.\n\nJSON Parse\n\nParses a JSON string into a JavaScript object or array.\n\nValue: vars.apiResponse.body\nAssign To: data\n\n  External API Call steps automatically parse JSON responses, so you usually do not need JSON Parse for API results. Use it for JSON stored in files or database fields.\n\nJSON Stringify\n\nConverts an object or array to a JSON string.\n\nValue: { users: vars.users, total: vars.count }\nPretty: true\nAssign To: jsonString\n\nBase64 Encode\n\nEncodes data to a Base64 string.\n\nValue: vars.fileContent\nAssign To: encoded\n\nBase64 Decode\n\nDecodes a Base64 string back to its original form.\n\nValue: vars.encodedData\nEncoding: utf-8\nAssign To: decoded\n\nURL Encode\n\nEncodes a string for safe use in URLs, replacing special characters with percent-encoded equivalents.\n\nValue: vars.searchQuery\nAssign To: encodedQuery\n\nResult: \"hello world\" becomes \"hello%20world\"\n\nURL Decode\n\nDecodes a percent-encoded URL string back to its original form.\n\nValue: query.q\nAssign To: searchTerm\n\nHTML Encode\n\nEncodes special characters as HTML entities to prevent XSS when rendering user content.\n\nValue: vars.userComment\nAssign To: safeComment\n\nResult: \"<script>alert('xss')</script>\" becomes \"&lt;script&gt;alert(&#39;xss&#39;)&lt;/script&gt;\"\n\nHTML Decode\n\nDecodes HTML entities back to their original characters.\n\nValue: vars.encodedContent\nAssign To: decodedContent\n\nThe JSON string to parse.\nVariable name to store the parsed result.\nThe value to serialize.\nWhether to format with indentation. Defaults to false.\nVariable name to store the JSON string.\nThe string or buffer to encode.\nVariable name to store the encoded string.\nThe Base64 string to decode.\nOutput encoding: `utf-8`, `binary`. Defaults to `utf-8`.\nVariable name to store the decoded result.\nThe string to encode.\nVariable name to store the encoded string.\nThe encoded string to decode.\nVariable name to store the decoded string.\nThe string to encode.\nVariable name to store the encoded string.\nThe HTML-encoded string to decode.\nVariable name to store the decoded string.\nSteps Reference\n/reference/steps\nBrowse all step categories\nCrypto Steps\n/reference/steps/crypto\nEncryption and hashing operations\nAPI Steps\n/reference/steps/api\nHTTP requests and responses"
  },
  {
    "path": "/reference/steps/file",
    "title": "File Steps",
    "description": "Steps for reading, writing, deleting, and managing files.",
    "section": "reference",
    "url": "https://docs.spala.ai/reference/steps/file/",
    "last_updated": "2026-07-08",
    "content": "File Steps\n\nFile steps handle file system operations -- reading, writing, listing, uploading, and downloading files.\n\n  File steps require a storage addon to be installed (Local Storage or S3 Storage). Configure your storage addon before using these steps.\n\nRead File\n\nReads the contents of a file from storage.\n\nPath: 'uploads/' + vars.filename\nEncoding: utf-8\nAssign To: fileContent\n\nWrite File\n\nWrites content to a file in storage. Creates the file if it does not exist, or overwrites it if it does.\n\nPath: 'reports/' + vars.reportId + '.json'\nContent: vars.reportData | toJSON\nEncoding: utf-8\n\nDelete File\n\nDeletes a file from storage.\n\nPath: 'uploads/' + vars.oldFilename\n\nList Directory\n\nLists all files and subdirectories within a directory.\n\nPath: 'uploads/images'\nRecursive: false\nAssign To: imageFiles\n\nUpload File\n\nHandles an incoming file upload from a multipart form request and saves it to storage.\n\nField Name: avatar\nDestination: 'uploads/avatars'\nMax Size: 5242880\nAllowed Types: ['image/png', 'image/jpeg', 'image/webp']\nAssign To: uploadedFile\n\nThe result stored in the assigned variable includes path, originalName, size, and mimetype.\n\nDownload File\n\nSends a file from storage as a download response to the client.\n\nPath: 'reports/' + vars.reportId + '.pdf'\nFilename: 'monthly-report.pdf'\nContent Type: application/pdf\n\nThe file path relative to the storage root.\nFile encoding: `utf-8`, `base64`, `binary`. Defaults to `utf-8`.\nVariable name to store the file contents.\nThe destination file path.\nThe data to write.\nFile encoding: `utf-8`, `base64`, `binary`. Defaults to `utf-8`.\nThe file path to delete.\nThe directory path to list.\nWhether to include files in subdirectories. Defaults to false.\nVariable name to store the array of file paths.\nThe form field name for the uploaded file.\nDirectory path to save the file. Defaults to `uploads/`.\nMaximum file size in bytes. Defaults to 10MB.\nArray of allowed MIME types (e.g., `[\"image/png\", \"image/jpeg\"]`).\nVariable name to store file metadata (path, size, mimetype).\nThe file path in storage.\nThe filename presented to the client. Defaults to the original filename.\nMIME type for the response. Auto-detected if not specified.\nSteps Reference\n/reference/steps\nBrowse all step categories\nUpload Files\n/guides/file-uploads\nStep-by-step file upload guide\nEncoding Steps\n/reference/steps/encoding\nBase64 encoding for file data"
  },
  {
    "path": "/reference/filters",
    "title": "Filters Reference",
    "description": "Complete reference for data transformation filters in Spala expressions.",
    "section": "reference",
    "url": "https://docs.spala.ai/reference/filters/",
    "last_updated": "2026-07-08",
    "content": "Filters Reference\n\nFilters transform values within Spala expressions. They are applied using the pipe (|) operator and can be chained together to perform multiple transformations in sequence.\n\nSyntax\n\nexpression | filterName\nexpression | filterName(arg1, arg2)\nexpression | filter1 | filter2 | filter3\n\nFilters read the value on the left side of the pipe, transform it, and pass the result to the next filter or use it as the final value.\n\nExamples\n\nvars.name | uppercase\n// \"john doe\" → \"JOHN DOE\"\n\nvars.items | map('name') | join(', ')\n// [{name: \"Apple\"}, {name: \"Banana\"}] → \"Apple, Banana\"\n\nvars.price | round(2)\n// 19.956 → 19.96\n\nvars.createdAt | format('YYYY-MM-DD')\n// Date → \"2026-03-06\"\n\nChaining\n\nFilters are evaluated left to right. Each filter receives the output of the previous one:\n\nvars.description | trim | lowercase | truncate(100)\n\nThis trims whitespace, converts to lowercase, then truncates to 100 characters.\n\n  Filter arguments can be literals, variable references, or nested expressions. For example: vars.items | slice(0, vars.pageSize).\n\nCategories\n\nString Filters\n/reference/filters/string\n\n    Transform text: uppercase, lowercase, trim, replace, split, slugify, and more.\n\nArray Filters\n/reference/filters/array\n\n    Manipulate arrays: map, filter, sort, unique, flatten, groupBy, and more.\n\nNumber Filters\n/reference/filters/number\n\n    Work with numbers: round, floor, ceil, clamp, abs, toFixed, and more.\n\nDate Filters\n/reference/filters/date\n\n    Format and manipulate dates: format, addDays, diffInDays, isAfter, and more.\n\nObject Filters\n/reference/filters/object\n\n    Transform objects: keys, values, pick, omit, merge, has, and more.\n\nType Filters\n/reference/filters/type\n\n    Convert and check types: toString, toNumber, toBoolean, isEmpty, typeOf, and more.\n\nSteps Reference\n/reference/steps\nStep types that use filters in expressions\nOperators\n/reference/operators\nOperators for use alongside filters in expressions\nData Types\n/reference/data-types\nSupported data types that filters transform"
  },
  {
    "path": "/reference/filters/number",
    "title": "Number Filters",
    "description": "Filters for rounding, clamping, and transforming numeric values.",
    "section": "reference",
    "url": "https://docs.spala.ai/reference/filters/number/",
    "last_updated": "2026-07-08",
    "content": "Number Filters\n\nNumber filters perform mathematical operations and numeric conversions.\n\nUsage examples\n\nFormat a price with two decimal places:\n\nvars.price | toFixed(2)\n// 9.9 → \"9.90\"\n\nClamp a pagination page number:\n\nvars.requestedPage | clamp(1, vars.totalPages)\n\nRound a calculated discount:\n\nvars.originalPrice * 0.15 | round(2)\n\nEnsure a quantity is at least 1:\n\ninput.quantity | max(1)\n\nNote that toFixed returns a string (for display), while round returns a number (for further calculations).\n\nFilters Reference\n/reference/filters\nBrowse all filter categories\nDate Filters\n/reference/filters/date\nFormat and manipulate dates\nType Filters\n/reference/filters/type\nConvert between data types"
  },
  {
    "path": "/reference/filters/object",
    "title": "Object Filters",
    "description": "Filters for transforming, querying, and manipulating objects.",
    "section": "reference",
    "url": "https://docs.spala.ai/reference/filters/object/",
    "last_updated": "2026-07-08",
    "content": "Object Filters\n\nObject filters work with key-value pairs -- extracting, merging, and reshaping objects.\n\nUsage examples\n\nStrip sensitive fields from a user object before responding:\n\nvars.user | omit(['password_digest', 'reset_token'])\n\nSafely access nested data with a default:\n\nvars.config | get('database.host', 'db.example.internal')\n\nMerge request body with defaults:\n\n{ page: 1, limit: 20 } | merge(input)\n\nBuild a response by picking specific fields:\n\nvars.order | pick(['id', 'status', 'total', 'created_at'])\n\nCheck if a required field exists:\n\ninput | has('email')\n\nFilters Reference\n/reference/filters\nBrowse all filter categories\nArray Filters\n/reference/filters/array\nTransform arrays of objects\nType Filters\n/reference/filters/type\nCheck and convert value types"
  },
  {
    "path": "/reference/operators",
    "title": "Operators",
    "description": "Reference for all expression operators available in Spala.",
    "section": "reference",
    "url": "https://docs.spala.ai/reference/operators/",
    "last_updated": "2026-07-08",
    "content": "Operators\n\nOperators are used within Spala expressions to perform calculations, comparisons, and logical operations. They work the same way as JavaScript operators.\n\nArithmetic operators\n\n| Operator | Description | Example | Result |\n|----------|-------------|---------|--------|\n| + | Addition (or string concatenation) | 5 + 3 | 8 |\n| - | Subtraction | 10 - 4 | 6 |\n| * | Multiplication | 6 * 7 | 42 |\n| / | Division | 15 / 4 | 3.75 |\n| % | Modulo (remainder) | 17 % 5 | 2 |\n\nThe + operator also concatenates strings:\n\n'Hello ' + vars.name\n// \"Hello Alice\"\n\nComparison operators\n\n| Operator | Description | Example | Result |\n|----------|-------------|---------|--------|\n| == | Equal to | vars.status == 'active' | true/false |\n| != | Not equal to | vars.role != 'admin' | true/false |\n| > | Greater than | vars.age > 18 | true/false |\n| < | Less than | vars.price < 100 | true/false |\n| >= | Greater than or equal | vars.count >= 10 | true/false |\n| <= | Less than or equal | vars.score <= 50 | true/false |\n\n  Spala uses == for equality comparisons (not ===). Type coercion applies, so '5' == 5 is true.\n\nLogical operators\n\n| Operator | Description | Example | Result |\n|----------|-------------|---------|--------|\n| && | Logical AND | vars.isAdmin && vars.isActive | true if both are true |\n| \\|\\| | Logical OR | vars.role == 'admin' \\|\\| vars.role == 'owner' | true if either is true |\n| ! | Logical NOT | !vars.isDeleted | Inverts the boolean |\n\nLogical operators short-circuit: && stops at the first falsy value, || stops at the first truthy value.\n\nTernary operator\n\nThe ternary operator provides inline conditional expressions:\n\ncondition ? valueIfTrue : valueIfFalse\n\nvars.count > 0 ? 'Items found' : 'No items'\n\nTernary expressions can be nested, but keep them simple for readability:\n\nvars.role == 'admin' ? 'Full access' : vars.role == 'editor' ? 'Edit access' : 'Read only'\n\nNullish coalescing operator\n\nThe ?? operator returns the right-hand value when the left-hand value is null or undefined (but not other falsy values like 0, '', or false).\n\nvars.config.timeout ?? 5000\n// Returns vars.config.timeout if it exists, otherwise 5000\n\nContrast with || which treats 0, '', and false as falsy:\n\nvars.count || 10    // If count is 0, returns 10\nvars.count ?? 10    // If count is 0, returns 0\n\nOptional chaining operator\n\nThe ?. operator safely accesses nested properties without throwing an error if an intermediate value is null or undefined.\n\nvars.user?.address?.city\n// Returns the city if user and address exist, otherwise undefined\n\nWithout optional chaining, accessing vars.user.address.city would throw an error if user or address is null.\n\nWorks with method calls too:\n\nvars.items?.map('name')\n\nOperator precedence\n\nOperators are evaluated in this order (highest to lowest):\n\n1. ?. -- optional chaining\n2. ! -- logical NOT\n3. *, /, % -- multiplication, division, modulo\n4. +, - -- addition, subtraction\n5. >, <, >=, <= -- comparisons\n6. ==, != -- equality\n7. && -- logical AND\n8. || -- logical OR\n9. ?? -- nullish coalescing\n10. ? : -- ternary\n\nUse parentheses to override precedence when needed:\n\n(vars.a + vars.b) * vars.c\n\nFilters Reference\n/reference/filters\nData transformation filters for expressions\nData Types\n/reference/data-types\nSupported data types in Spala expressions"
  },
  {
    "path": "/reference/steps/other",
    "title": "Other Steps",
    "description": "Steps for sending emails, adding delays, executing sub-flows, custom code, and emitting events.",
    "section": "reference",
    "url": "https://docs.spala.ai/reference/steps/other/",
    "last_updated": "2026-07-08",
    "content": "Other Steps\n\nAdditional step types for email, timing, sub-flows, raw code, and event-driven patterns.\n\nSend Email\n\nSends an email using a configured email addon (SMTP or a provider like SendGrid).\n\nTo: vars.user.email\nSubject: 'Your order #' + vars.order.id + ' has been confirmed'\nBody: '<h1>Order Confirmed</h1><p>Thank you for your purchase!</p>'\n\n  The email addon must be installed and configured before using the Send Email step. See the Send Emails guide for setup instructions.\n\nSleep/Delay\n\nPauses flow execution for a specified duration. Useful for rate limiting, retry delays, or timed sequences.\n\nDuration: 2000\n\nThis pauses the flow for 2 seconds before continuing to the next step.\n\nExecute Flow\n\nCalls another flow as a sub-flow, passing data in and receiving a result back. This enables modular flow design by breaking complex logic into reusable pieces.\n\nFlow: validateOrder\nInput: { items: vars.cartItems, userId: vars.userId }\nAssign To: validationResult\n\nCustom Code\n\nExecutes raw JavaScript code within the flow. Use this for logic that cannot be expressed with the built-in steps.\n\n// Access variables, request, and environment\nconst items = vars.cartItems;\nconst total = items.reduce((sum, item) => sum + item.price * item.qty, 0);\nconst tax = total * 0.08;\nreturn { subtotal: total, tax, total: total + tax };\n\n  Custom Code steps cannot be converted back to visual steps. Prefer built-in steps when possible to maintain full visual editing capability.\n\nEmit Event\n\nEmits a named event that can be consumed by event listeners, WebSocket channels, or other flows subscribed to the event.\n\nEvent: order.created\nData: { orderId: vars.order.id, userId: vars.userId }\nChannel: 'user_' + vars.userId\n\nRecipient email address or array of addresses.\nEmail subject line.\nEmail body content. Supports HTML.\nSender email address. Defaults to the addon configuration.\nCC recipients.\nBCC recipients.\nReply-to email address.\nTime to wait in milliseconds.\nThe name or ID of the flow to execute.\nData to pass as input to the sub-flow. Accessible as `input` in the target flow.\nVariable name to store the sub-flow return value.\nJavaScript code to execute in the managed Spala runtime. Use documented flow variables like `input`, `params`, `query`, `headers`, `vars`, and `env`.\nVariable name to store the return value of the code.\nThe event name to emit.\nData payload to include with the event.\nOptional channel to scope the event to specific WebSocket subscribers.\nSteps Reference\n/reference/steps\nBrowse all step categories\nEvent Triggers\n/triggers/event-triggers\nReact to emitted events with trigger flows\nBackground Tasks\n/tasks/background-tasks\nRun sub-flows asynchronously"
  },
  {
    "path": "/reference/steps",
    "title": "Steps Reference",
    "description": "Complete reference for all step types available in Spala flows.",
    "section": "reference",
    "url": "https://docs.spala.ai/reference/steps/",
    "last_updated": "2026-07-08",
    "content": "Steps Reference\n\nSteps are the building blocks of Spala flows. Each step performs a specific action -- assigning a variable, querying a database, calling an API, or controlling the flow of execution. You build backend logic by dragging steps onto the canvas and configuring their properties.\n\nSpala provides step types organized into the following categories.\n\nCategories\n\nVariables\n/reference/steps/variables\n\n    Set variables, log output, return values, and add comments to document your flows.\n\nDatabase\n/reference/steps/database\n\n    Query, insert, update, delete, and aggregate data using built-in database steps.\n\nControl Flow\n/reference/steps/control-flow\n\n    Conditional branching, loops, error handling, and flow control with if/else, switch, for each, try/catch, and more.\n\nAPI\n/reference/steps/api\n\n    Make external HTTP requests, send responses, set headers, cookies, and redirects.\n\nAuth\n/reference/steps/auth\n\n    Generate and verify JWTs, hash and verify passwords, and create API keys.\n\nCrypto\n/reference/steps/crypto\n\n    Encrypt, decrypt, hash data, and generate UUIDs, random numbers, and random strings.\n\nFile\n/reference/steps/file\n\n    Read, write, delete, and list files. Handle uploads and downloads.\n\nEncoding\n/reference/steps/encoding\n\n    Parse and stringify JSON, encode and decode Base64, URLs, and HTML entities.\n\nOther\n/reference/steps/other\n\n    Send emails, add delays, execute sub-flows, write custom code, and emit events.\n\nHow steps work\n\nEvery step in a flow executes sequentially from top to bottom. Each step can read variables set by previous steps using expressions like vars.myVariable. Steps that produce output can assign their result to a variable for use downstream.\n\nSet Variable → Database Select → Loop → Respond\n\nAll step properties support expressions -- dynamic values that reference variables, use operators, and apply filters. See the Operators and Filters reference for details.\n\nFilters Reference\n/reference/filters\nData transformation filters for step expressions\nOperators\n/reference/operators\nArithmetic, comparison, and logical operators\nFunctions\n/flows\nLearn how to build backend logic with steps"
  },
  {
    "path": "/reference/filters/string",
    "title": "String Filters",
    "description": "Filters for transforming and manipulating text strings.",
    "section": "reference",
    "url": "https://docs.spala.ai/reference/filters/string/",
    "last_updated": "2026-07-08",
    "content": "String Filters\n\nString filters transform text values. Apply them with the pipe operator on any string expression.\n\nUsage examples\n\nFormat a URL slug from user input:\n\nvars.title | trim | slugify\n// \"  My Blog Post!  \" → \"my-blog-post\"\n\nBuild a display name:\n\nvars.firstName | capitalize + ' ' + vars.lastName | capitalize\n\nCheck if an email domain is allowed:\n\nvars.email | split('@') | last | lowercase\n// \"User@Example.COM\" → \"example.com\"\n\nFilters Reference\n/reference/filters\nBrowse all filter categories\nArray Filters\n/reference/filters/array\nTransform and manipulate arrays\nType Filters\n/reference/filters/type\nConvert between data types"
  },
  {
    "path": "/reference/filters/type",
    "title": "Type Filters",
    "description": "Filters for converting between types and checking values.",
    "section": "reference",
    "url": "https://docs.spala.ai/reference/filters/type/",
    "last_updated": "2026-07-08",
    "content": "Type Filters\n\nType filters convert values between types and check for null, undefined, or empty states.\n\nBehavior details\n\nisEmpty returns true for:\n- Empty strings ''\n- Empty arrays []\n- Empty objects {}\n- null and undefined\n\ndefault returns the fallback value when the input is null, undefined, or '' (empty string). Otherwise returns the original value.\n\ntoArray wraps non-array values in an array. If the value is already an array, it is returned unchanged.\n\nUsage examples\n\nProvide a fallback for missing query parameters:\n\nquery.page | toNumber | default(1)\n\nValidate that required fields are not empty:\n\ninput.name | isEmpty\n// Use in an If/Else condition to return a 400 error\n\nDetermine type for dynamic handling:\n\nvars.input | typeOf\n// 'string', 'number', 'object', 'array', etc.\n\nEnsure a value is always an array:\n\nvars.tags | toArray\n// \"javascript\" → [\"javascript\"]\n// [\"javascript\", \"node\"] → [\"javascript\", \"node\"]\n\nFilters Reference\n/reference/filters\nBrowse all filter categories\nData Types\n/reference/data-types\nAll supported data types in Spala\nString Filters\n/reference/filters/string\nTransform text values"
  },
  {
    "path": "/reference/steps/variables",
    "title": "Variable Steps",
    "description": "Steps for setting variables, logging output, returning values, and adding comments.",
    "section": "reference",
    "url": "https://docs.spala.ai/reference/steps/variables/",
    "last_updated": "2026-07-08",
    "content": "Variable Steps\n\nVariable steps handle data assignment, output, and documentation within your flows.\n\nSet Variable\n\nAssigns a value to a variable that can be referenced by subsequent steps.\n\nVariable Name: userId\nValue: input.id\n\nAfter this step, vars.userId holds the value from the request input.\n\nLog\n\nOutputs a value to the console for debugging purposes. Log output appears in the Spala console panel during flow execution.\n\nMessage: 'User created: ' + vars.userId\n\n  Log steps are helpful during development but consider removing them before deploying to production to keep your logs clean.\n\nReturn\n\nEnds the flow and returns a value. In endpoint flows, the returned value becomes the response body unless a Respond step has already sent a response.\n\nValue: { success: true, data: vars.result }\n\nComment\n\nAdds a documentation note to the flow. Comment steps do not execute any logic -- they exist purely to help you and your team understand the flow.\n\nText: Validate the incoming request before processing\n\n  Comments are preserved in generated code as inline code comments, so they carry over even when you switch to code view.\n\nThe name of the variable to set. Accessed later as `vars.variableName`.\nThe value to assign. Can be a literal, expression, or the result of a filter chain.\nThe value or message to log. Expressions are evaluated before logging.\nThe value to return from the flow.\nThe comment text to display on the canvas.\nSteps Reference\n/reference/steps\nBrowse all step categories\nControl Flow Steps\n/reference/steps/control-flow\nConditional branching and loops\nDatabase Steps\n/reference/steps/database\nQuery and modify database records"
  },
  {
    "path": "/self-hosting/deployment",
    "title": "Legacy Publish Link",
    "description": "This compatibility page points users to project publishing docs.",
    "section": "self-hosting",
    "url": "https://docs.spala.ai/self-hosting/deployment/",
    "last_updated": "2026-07-08",
    "content": "Legacy Publish Link\n\nSpala users publish project changes from the hosted dashboard. This page is kept only for old self-hosting links; the current guide is Publish.\n\nUse Publish for your backend\n\n  The deployment action users normally need is Publish. Publishing applies your project changes and makes the generated API available.\n\nPublish Flow\n\nBuild or edit your backend\n\n    Use AI Copilot, Lite mode, or Advanced mode to create tables, functions, endpoints, tasks, triggers, channels, and settings.\n\nReview the project\n\n    Inspect the project graph, endpoint list, API Playground, and generated docs before publishing.\n\nPublish\n\n    Click Publish in the dashboard. Spala applies the project changes and updates the live API.\n\nUse the API\n\n    Connect your frontend, use the generated endpoint snippets, or let an authenticated AI agent continue work through the project MCP.\n\nPlatform Operations\n\nSpala manages the hosted platform runtime and dashboard delivery. Public docs focus on publishing your project API from the dashboard.\n\nPublish\n/deployment/publish\nPublish generated backend changes\nPublish Your API\n/features/publish-export\nReview and publish project changes\nAPI Playground\n/features/playground\nTest endpoints before and after publishing\nAI Copilot\n/features/ai-assistant\nBuild the backend from a plain-language prompt\nSpala Public MCP\n/agents/mcp\nLet agents discover and hand off to the right project MCP"
  },
  {
    "path": "/self-hosting/configuration",
    "title": "Project Configuration",
    "description": "This compatibility page points users to project configuration docs.",
    "section": "self-hosting",
    "url": "https://docs.spala.ai/self-hosting/configuration/",
    "last_updated": "2026-07-08",
    "content": "Project Configuration\n\nSpala project configuration happens in the dashboard. This page is kept for old links; the current guide is Project Configuration.\n\nConfigure In The Dashboard\n\n- Settings — Project name, API docs, AI access, MCP, environment variables, snapshots, and publish controls\n- Addons — Integration-specific API keys and settings\n- Security/Auth — Endpoint authentication and user access patterns\n- AI Data Access — Which tables and fields AI Ask mode can read\n- MCP — Project MCP access for authenticated AI agents\n\nKeep secrets out of frontend code\n\n  Store integration secrets as project environment variables or addon settings in Spala. Do not expose secret keys in browser code.\n\nProject Configuration\n/deployment/configuration\nConfigure project settings and delivery tools\nSettings\n/settings\nConfigure project-level behavior\nAddons\n/addons\nConfigure integrations and required API keys\nAI Copilot\n/features/ai-assistant\nConfigure AI data access and project context\nSpala Public MCP\n/agents/mcp\nConnect agents through the public MCP handoff"
  },
  {
    "path": "/self-hosting/postgresql",
    "title": "Project Database",
    "description": "This compatibility page points users to project database docs.",
    "section": "self-hosting",
    "url": "https://docs.spala.ai/self-hosting/postgresql/",
    "last_updated": "2026-07-08",
    "content": "Project Database\n\nIn Spala, users manage project data models from the visual Database screen. This page is kept for old links; the current guide is Project Database.\n\nYou do not need to provision or maintain database infrastructure to use the hosted Spala dashboard.\n\nUse the visual database designer\n\n  Start in Database to add tables and relationships. Spala handles the managed database layer behind the scenes.\n\nWhat Users Configure\n\n- Tables and fields\n- Relationships between tables\n- Data access from functions and endpoints\n- Database triggers\n- Import, export, and review workflows exposed in the dashboard\n\nProject Database\n/deployment/database\nUnderstand project database management\nDatabase\n/database\nCreate tables, fields, and relationships visually\nTables & Fields\n/database/tables\nDesign your project schema\nDatabase Triggers\n/triggers/database-triggers\nRun logic when records change\nDatabase Steps\n/reference/steps/database\nUse project data inside functions"
  },
  {
    "path": "/self-hosting/installation",
    "title": "Start a Project",
    "description": "This compatibility page points users to the hosted project creation flow.",
    "section": "self-hosting",
    "url": "https://docs.spala.ai/self-hosting/installation/",
    "last_updated": "2026-07-08",
    "content": "Start a Project\n\nThere is no local installation flow required to use Spala. This page is kept for old links; the current guide is Start a Project.\n\nHosted dashboard\n\n  New users should open https://dashboard.spala.ai/signup. Existing users can sign in from the dashboard and create or open a project.\n\nFirst Project\n\nOpen the dashboard\n\n    Create an account at https://dashboard.spala.ai/signup, or sign in if you already have one.\n\nCreate a project\n\n    Click New Project and name the backend you want to build.\n\nDescribe what you need\n\n    Use AI Copilot in Lite mode to describe the app feature or API you want.\n\nInspect and publish\n\n    Review the generated project graph, test endpoints in the Playground, and publish when ready.\n\nStart a Project\n/deployment/start\nCreate a project from the hosted dashboard\nQuick Start\n/getting-started/quickstart\nBuild a simple API in the dashboard\nAI Copilot\n/features/ai-assistant\nUse plain language to generate backend resources\nThe Interface\n/getting-started/interface\nLearn the dashboard layout"
  },
  {
    "path": "/settings/cors-security",
    "title": "CORS and Security",
    "description": "Configure frontend origins, credentialed browser requests, trusted hosts, and publish requirements for Spala project APIs.",
    "section": "settings",
    "url": "https://docs.spala.ai/settings/cors-security/",
    "last_updated": "2026-07-08",
    "content": "CORS and Security\n\nUse this page when a browser frontend, mobile app, or external UI needs to call a Spala project API.\n\nOwner Checklist\n\n1. Open the project in the hosted dashboard.\n2. Go to Settings → Security.\n3. Add the exact frontend origin in CORS Allowed Origins.\n4. Include the scheme, host, and port, such as https://app.example.com or http://localhost:5173.\n5. Do not include URL paths.\n6. Allow the request headers the frontend sends, usually Authorization and Content-Type.\n7. Enable credentialed requests only when the project intentionally uses cookie sessions.\n8. Save the settings.\n9. Publish the backend API before asking the frontend developer to retest.\n\nPublish after security changes\n\n  Saved CORS/security settings do not help the live API until the project is published. If curl works but browser requests fail, confirm the origin is exact and the latest settings are published.\n\nBrowser Rules\n\nBrowser CORS uses the page origin, not the API route:\n\nhttps://app.example.com\nhttp://localhost:5173\n\nDo not add paths such as https://app.example.com/dashboard.\n\nIf the frontend sends a bearer token, allow Authorization. If it sends JSON, allow Content-Type. If it uses cookies, the response must use the exact origin, not *, and must include credential support.\n\nCookie Sessions\n\nCookie sessions need a tighter contract than bearer tokens. Include these fields in the frontend handoff:\n\n| Field | What to specify |\n| --- | --- |\n| Credential mode | include, same-origin, or none |\n| Cookie SameSite | Lax, Strict, or None |\n| Cookie Secure | Required for cross-site HTTPS cookies |\n| Domain/path | Which frontend and API hosts receive the cookie |\n| CSRF | Header name and source, or not required |\n| Logout | Local clear, server revoke, cookie expiry, or a combination |\n\nVerification\n\nAfter publishing, test one preflight-shaped browser request and one real request from the frontend origin.\n\nOPTIONS /api/cases HTTP/1.1\nOrigin: https://app.example.com\nAccess-Control-Request-Method: POST\nAccess-Control-Request-Headers: authorization, content-type\n\nExpected shape:\n\nAccess-Control-Allow-Origin: https://app.example.com\nAccess-Control-Allow-Methods: GET, POST, PUT, PATCH, DELETE, OPTIONS\nAccess-Control-Allow-Headers: Authorization, Content-Type\n\nFor credentialed requests:\n\nAccess-Control-Allow-Credentials: true\n\nHandoff Fields\n\nAdd these fields to External Frontend Handoff:\n\n- Frontend origin\n- Whether the origin is saved in Settings → Security\n- Whether the backend was republished after the change\n- Required request headers\n- Whether credentials are enabled\n- Cookie and CSRF details if used\n\nExternal Frontend Handoff\n/guides/frontend-handoff\nCopy the complete frontend packet\nFrontend Integration Cookbook\n/guides/frontend-integration\nUse CORS with auth, uploads, and realtime\nPublish\n/features/publish-export\nPublish security changes to the live API\nAPI Docs\n/features/api-docs\nShare REST contracts with frontend builders"
  },
  {
    "path": "/tasks/background-tasks",
    "title": "Background Tasks",
    "description": "Execute long-running operations asynchronously without blocking API responses in Spala.",
    "section": "tasks",
    "url": "https://docs.spala.ai/tasks/background-tasks/",
    "last_updated": "2026-07-08",
    "content": "Background Tasks\n\nBackground tasks let you offload long-running or resource-intensive operations from your API endpoints. Instead of making users wait for a slow operation to complete, your endpoint can queue a background task and return an immediate response.\n\nSpala Background Tasks monitor showing task status, filters, logs, and retry controls\nThe Background Tasks monitor shows queued, running, completed, failed, stopped, and timed-out work.\n\nHow Background Tasks Work\n\nWhen a function triggers a background task:\n\n1. The task is queued for asynchronous execution\n2. The calling function continues immediately without waiting\n3. The background task runs independently in its own execution context\n4. Results and errors are logged for later inspection\n\nThis pattern is essential for operations like sending bulk emails, processing uploaded files, generating reports, or calling slow external APIs.\n\nCreating a Background Task\n\nNavigate to the Tasks section in the left sidebar\n\nClick New Task and select Background Task\n\nGive the task a descriptive name\n\nBuild the function that defines what the task does\n\nSave the task\n\nTriggering a Background Task\n\nBackground tasks are triggered from within other functions using the Run Task step. You can pass input data to the task as parameters.\n\n// In your endpoint function:\n\n// Step 1: Validate the upload\n// ... validation logic ...\n\n// Step 2: Run Task - \"Process Upload\"\n// Pass the file path and user ID as parameters\n// { filePath: vars.uploadPath, userId: vars.currentUser.id }\n\n// Step 3: Respond to Client\n// Return immediately with a 202 Accepted status\n// { message: \"Upload is being processed\", taskId: result.taskId }\n\nIn the visual editor, drag a Run Task step into your function, select the target task, and map any input variables.\n\nPassing Data to Background Tasks\n\nWhen you trigger a background task, you can pass an input object that becomes available inside the task function as vars.input. This lets you provide all the context the task needs to do its work.\n\n// Endpoint function - trigger the task with input data\n// Run Task: \"Send Welcome Email\"\n// Input: { email: input.email, name: input.name }\n\n// Inside the background task function:\n// vars.input.email → \"user@example.com\"\n// vars.input.name  → \"Jane Smith\"\n\n  The input data is serialized when the task is queued, so it must be JSON-serializable. Database connections, file handles, and other non-serializable values cannot be passed to background tasks.\n\nTask Configuration\n\nExample: Bulk Email Campaign\n\nAn endpoint that accepts a list of recipients and sends emails in the background:\n\n// POST /api/campaigns/send\n\n// Step 1: Database Query - Get campaign details\n// SELECT * FROM campaigns WHERE id = input.campaignId\n\n// Step 2: Run Task - \"Send Campaign Emails\"\n// Input: { campaignId: vars.campaign.id, recipients: input.recipients }\n\n// Step 3: Update campaign status\n// UPDATE campaigns SET status = 'sending' WHERE id = input.campaignId\n\n// Step 4: Respond\n// { status: \"sending\", recipientCount: input.recipients.length }\n\nThe background task function:\n\n// Background Task: \"Send Campaign Emails\"\n\n// Step 1: Database Query - Get campaign content\n// SELECT * FROM campaigns WHERE id = input.campaignId\n\n// Step 2: Loop - For each recipient\n//   Step 3: Send Email (addon step)\n//   to: recipient.email, subject: campaign.subject, body: campaign.html\n\n// Step 4: Update campaign status\n// UPDATE campaigns SET status = 'sent', sent_at = NOW()\n// WHERE id = input.campaignId\n\nExample: Image Processing Pipeline\n\nProcess an uploaded image through multiple transformations without blocking the upload response:\n\n// POST /api/images/upload\n\n// Step 1: Save original file to storage\n// Step 2: Run Task - \"Process Image\"\n//   Input: { imageId: vars.savedImage.id, path: vars.savedImage.path }\n// Step 3: Respond with 202 Accepted\n\n  Background tasks run in a separate execution context. They do not have access to the original request headers, cookies, or authentication context. Pass any required data explicitly through the task input.\n\nMonitoring Background Tasks\n\nThe Background Tasks monitor shows the status of all background task executions:\n\n- Queued — Waiting to start\n- Running — Currently executing\n- Completed — Finished successfully\n- Failed — Terminated with an error (check logs for details)\n- Stopped — Manually stopped before completion\n- Timed Out — Exceeded the configured timeout\n\nEach execution record includes the input data, start/end times, duration, and any error output, giving you full visibility into what happened during each run.\n\nFrom the monitor you can:\n\n- Filter by status or addon\n- Track running jobs against max concurrency\n- Refresh job state\n- Stop a running task when it is no longer needed\n- Retry failed work after fixing configuration or data\n- Open a detail view with prompt, result, and logs\n\nA descriptive name for the task\nMaximum execution time in seconds (default: 300)\nAutomatically retry if the task fails\nMaximum retry attempts before marking as failed (default: 3)\nSeconds to wait between retry attempts (default: 10)\nScheduled Tasks\n/tasks/scheduled-tasks\nRun functions on a repeat schedule or manually\nTasks Overview\n/tasks\nLearn about all task types in Spala"
  },
  {
    "path": "/tasks/scheduled-tasks",
    "title": "Scheduled Tasks",
    "description": "Run backend functions automatically on a recurring interval or manually on demand.",
    "section": "tasks",
    "url": "https://docs.spala.ai/tasks/scheduled-tasks/",
    "last_updated": "2026-07-08",
    "content": "Scheduled Tasks\n\nScheduled tasks run backend functions automatically at a recurring interval. Use them for cleanup jobs, report generation, external API syncs, periodic health checks, and other work that should happen without a user request.\n\nCreate A Task\n\nOpen Tasks from the sidebar\n\nClick Create Task\n\nGive the task a clear name, like \"Daily Cleanup\" or \"Sync Inventory\"\n\nAdd a schedule by choosing a frequency and unit, such as every 5 minutes, every hour, or every day\n\nOptionally set start and end times\n\nBuild the function steps that should run on each execution\n\nSave and enable the task\n\nManual Tasks\n\nIf a task should only run when another function starts it, remove the schedule and keep it manual. Manual tasks are useful for reusable jobs such as \"Send Report\", \"Process Upload\", or \"Rebuild Search Index\".\n\nBackground Execution\n\nTasks can run in the main API process or in a background process.\n\nUse Run in Background when the task may be slow, resource-heavy, or safe to queue. Background execution runs in an isolated process with configurable timeout and memory limits.\n\nBackground work can be delayed\n\n  Background tasks run through a queue. Use them for async work, not for request-response behavior that must finish immediately.\n\nTask Configuration\n\nExamples\n\nDaily cleanup\n\n// Schedule: every 1 day\n// Step 1: Delete expired sessions\n// Step 2: Delete old temporary files\n// Step 3: Log cleanup counts\n\nHourly inventory sync\n\n// Schedule: every 1 hour\n// Step 1: Call supplier API\n// Step 2: Loop through products\n// Step 3: Update local inventory rows\n\nManual export job\n\n// No schedule\n// Triggered by an admin endpoint or another function\n// Step 1: Query records\n// Step 2: Generate file\n// Step 3: Send download link\n\nError Handling\n\nWhen a task fails, Spala records the run with diagnostic details. If the task runs in the background, inspect it from the Background Tasks monitor to see status, output, and logs.\n\nA descriptive name for the task\nHow often the task should run\nSeconds, minutes, hours, or days\nOptional time when the schedule becomes active\nOptional time when the schedule stops running\nWhether the task is active and will run on schedule\nRun in an isolated background process instead of the main API process\nOptional timeout and memory limits for background execution\nBackground Tasks\n/tasks/background-tasks\nMonitor queued, running, completed, and failed background work\nTasks Overview\n/tasks\nLearn about all task types in Spala\nProject Agents\n/features/project-agents\nUse agents for richer triggered automations"
  },
  {
    "path": "/triggers/database-triggers",
    "title": "Database Triggers",
    "description": "Execute functions automatically when rows are inserted, updated, or deleted on your Spala tables.",
    "section": "triggers",
    "url": "https://docs.spala.ai/triggers/database-triggers/",
    "last_updated": "2026-07-08",
    "content": "Database Triggers\n\nDatabase triggers run backend logic when project data changes. Use them when the action should happen no matter which endpoint, task, or agent changed the data.\n\nTrigger Events\n\nEach trigger is configured for a table and one or more events:\n\n| Event | When It Fires | Common Use |\n|---|---|---|\n| Insert | A new row is created | Send welcome emails, create related records |\n| Update | An existing row changes | Audit changes, sync external systems |\n| Delete | A row is removed | Cleanup related data, notify systems |\n\nTriggers can run synchronously or asynchronously depending on the setting in the editor.\n\nCreate A Database Trigger\n\nOpen Triggers from the sidebar\n\nClick Create Trigger\n\nSelect the target table\n\nChoose insert, update, delete, or multiple events\n\nChoose whether the trigger runs sync or async\n\nBuild the trigger function with Gherkin, Visual, Split, or Code view\n\nSave and enable the trigger\n\nTrigger Variables\n\nInside the trigger function, Spala provides variables for the changed row:\n\nExamples\n\nAudit log\n\n// Update on users\n// Step 1: Insert audit row with trigger.oldRecord and trigger.newRecord\n// Step 2: Return success\n\nWelcome email\n\n// Insert on users\n// Step 1: Send email to trigger.newRecord.email\n// Step 2: Log delivery result\n\nCleanup\n\n// Delete on projects\n// Step 1: Remove related temporary records\n// Step 2: Notify operations channel\n\nUse endpoints for request validation\n\n  If you need to reject invalid client input before a write, put that validation in the endpoint function that performs the write. Use database triggers for automatic follow-up work around data changes.\n\nThe operation type: INSERT, UPDATE, or DELETE\nThe new row data, available on insert and update\nThe previous row data, available on update and delete\nThe table/model ID that fired the trigger\nTriggers Overview\n/triggers\nCreate trigger automations from the current builder\nDatabase Steps\n/reference/steps/database\nReference for database query steps used in trigger functions\nProject Agents\n/features/project-agents\nBuild richer automations with multiple trigger types"
  },
  {
    "path": "/triggers/event-triggers",
    "title": "Event Triggers",
    "description": "Availability note for event-driven workflows in Spala.",
    "section": "triggers",
    "url": "https://docs.spala.ai/triggers/event-triggers/",
    "last_updated": "2026-07-08",
    "content": "Event Triggers\n\nAdvanced availability\n\n  The current builder focuses on database triggers. If Event Triggers are not visible in your workspace, use database triggers, scheduled tasks, project agents, or explicit function calls for event-driven workflows.\n\nEvent-driven workflows are still useful in Spala, but the visible product surface you should rely on today is:\n\nDatabase Triggers\nRun a function when records are inserted, updated, or deleted.\n/triggers/database-triggers\n\nScheduled Tasks\nRun recurring or manual backend jobs.\n/tasks/scheduled-tasks\n\nProject Agents\nRun agent workflows from manual, webhook, schedule, database change, or websocket triggers.\n/features/project-agents\n\nRun Function\nCall reusable backend logic from endpoints, tasks, triggers, or other functions.\n/flows\n\nPractical Alternatives\n\n- Use a database trigger when the workflow starts from a record change.\n- Use a scheduled task when the workflow starts from time or manual admin action.\n- Use a project agent when the workflow needs AI reasoning, chat, or multi-step agent execution.\n- Use Run Function when one backend action should call another reusable function directly.\n\nDatabase Triggers\n/triggers/database-triggers\nFire functions on insert, update, and delete operations\nTriggers Overview\n/triggers\nUnderstand visible trigger workflows\nProject Agents\n/features/project-agents\nUse agent workflows for richer event handling"
  }
]