{
  "description": "Motorical Agent Hub machine twin. Generated from docs-site/data/journeys.yaml plus the live scope catalog — do not hand-edit. Human-readable version: https://docs.motorical.com/agents",
  "coreDefinition": "The email API for developers with multiple projects: one isolated sending stream (a Motor Block) per project.",
  "motorBlock": {
    "canonicalTerm": "Motor Block",
    "definition": "A Motor Block is Motorical's SMTP sending block: one isolated sending stream per project, with its own domain, credentials, limits, logs, webhooks and analytics.",
    "aliases": [
      "SMTP sending block",
      "sending stream",
      "per-project sending stream",
      "email sending project"
    ],
    "nearestEquivalentsAtOtherProviders": [
      "Postmark Servers",
      "SendGrid subusers",
      "Mailgun subaccounts"
    ]
  },
  "scopes": {
    "send:transactional": {
      "resource": "transactional",
      "level": "manage",
      "agentDescription": "Send transactional email through the caller's Motor Block(s)."
    },
    "read:analytics": {
      "resource": "analytics",
      "level": "read",
      "agentDescription": "Read delivery logs, analytics, and usage for the caller's Motor Block(s). Never includes unmasked recipient PII (logs.pii) regardless of what is requested."
    },
    "manage:domains": {
      "resource": "domains",
      "level": "manage",
      "agentDescription": "Add, verify, and read sending domains and their DNS health. Includes everything read:domains grants."
    },
    "manage:motor-blocks": {
      "resource": "motor-blocks",
      "level": "manage",
      "agentDescription": "List and manage the ordinary lifecycle of production Motor Blocks: create, rename, change type, assign domain, deactivate, reactivate, and delete. Includes no credential values and cannot change the active auth method."
    },
    "manage:sandbox": {
      "resource": "sandbox",
      "level": "manage",
      "agentDescription": "Check, provision, and convert the developer sandbox. Allowlisting by 6-digit code is dashboard-only and not available over OAuth; to allow recipients over OAuth use the manage:recipients scope."
    },
    "manage:recipients": {
      "resource": "recipients",
      "level": "manage",
      "agentDescription": "Request permission to send test email to an address you name, and see which addresses have confirmed or stopped. The recipient confirms by clicking an emailed link; an agent can never confirm on their behalf."
    },
    "manage:webhooks": {
      "resource": "webhooks",
      "level": "manage",
      "agentDescription": "Create, update, delete, and test webhook endpoints on the caller's Motor Block(s). Includes everything read:webhooks grants."
    },
    "read:webhooks": {
      "resource": "webhooks",
      "level": "read",
      "agentDescription": "List webhook endpoints and read their delivery history and stats, without creating, updating, deleting, or testing any."
    },
    "read:domains": {
      "resource": "domains",
      "level": "read",
      "agentDescription": "List and read sending domains, without adding or verifying any."
    }
  },
  "journeys": [
    {
      "key": "A",
      "audience": "Coding assistant adding email to an app",
      "status": "real",
      "route": "Local server (API keys) or the HTTP API directly",
      "steps": [
        {
          "id": "A1",
          "description": "Get a Motor Block API key (mk_live_...) from the dashboard, or use the developer sandbox for a zero-credential start (see Journey C).",
          "humanRequired": true,
          "sayToHuman": "I need a Motor Block API key from your Motorical dashboard (or we can start with the free sandbox — no account needed yet)."
        },
        {
          "id": "A2",
          "description": "Dry-run the send payload before sending anything real.",
          "tool": "motorical_send_email",
          "endpoint": "POST /v1/send (dryRun: true)",
          "humanRequired": false
        },
        {
          "id": "A3",
          "description": "Remove dryRun (or set it false) to send for real.",
          "tool": "motorical_send_email",
          "endpoint": "POST /v1/send",
          "humanRequired": false
        },
        {
          "id": "A4",
          "description": "Check delivery status/events for a sent message, or wait for a terminal outcome rather than treating a 202 as delivered.",
          "tools": [
            "motorical_get_send_status",
            "motorical_get_message",
            "motorical_get_message_events",
            "motorical_wait_for_outcome"
          ],
          "humanRequired": false
        },
        {
          "id": "A5",
          "description": "Optional: register a webhook so the app learns about deliveries, bounces, and complaints without polling.",
          "tool": "motorical_webhook_create",
          "humanRequired": false,
          "note": "Available on both the local server and hosted OAuth's dedicated webhooks server (read:webhooks/manage:webhooks scopes) — not local-only."
        }
      ]
    },
    {
      "key": "B",
      "audience": "Operator agent running a customer's account over MCP",
      "status": "real",
      "route": "Hosted MCP over OAuth (a person present for consent)",
      "steps": [
        {
          "id": "B1",
          "description": "Connect to the hosted MCP server (main, for a full setup on one consent, or a narrow scoped server for least privilege).",
          "humanRequired": true,
          "sayToHuman": "I'll connect to Motorical over OAuth — you'll see a browser consent screen asking which scopes to grant."
        },
        {
          "id": "B2",
          "description": "Read live account state before acting.",
          "tools": [
            "motorical_get_onboarding_state",
            "motorical_list_motor_blocks"
          ],
          "humanRequired": false
        },
        {
          "id": "B3",
          "description": "Operate within the granted scope — send email, read analytics, manage domains, manage Motor Blocks, or manage webhooks, depending on which scopes were granted.",
          "tools": [
            "motorical_send_email",
            "motorical_get_overview",
            "motorical_get_deliverability",
            "motorical_motor_block_list",
            "motorical_motor_block_create"
          ],
          "humanRequired": false
        },
        {
          "id": "B4",
          "description": "A consequential operation (type change, domain assignment, deactivate, delete) asks for confirmation bound to the exact arguments shown — never assume consent carries to different arguments.",
          "tools": [
            "motorical_motor_block_change_type",
            "motorical_motor_block_assign_domain",
            "motorical_motor_block_deactivate",
            "motorical_motor_block_delete"
          ],
          "humanRequired": true,
          "sayToHuman": "This is a consequential change (e.g. permanently deleting a Motor Block) — confirming means exactly this action, nothing broader.",
          "stopCondition": "Deactivate is reversible; delete is not. Never treat them as interchangeable, and never skip the confirmation step."
        }
      ]
    },
    {
      "key": "C",
      "audience": "Agent in a chat client (Claude Code, Claude Desktop, another MCP client) whose human has no account",
      "status": "real",
      "route": "Connect the main motorical server and authenticate it; the person creates the account in the browser. Then Journey B.",
      "steps": [
        {
          "id": "C1",
          "description": "Connect the main hosted server (https://mcp.motorical.com/v1/motorical/mcp) in the client and run the client's authenticate step (for Claude Code: claude mcp add --transport http motorical https://mcp.motorical.com/v1/motorical/mcp in a terminal, then /mcp in the session, pick motorical, Authenticate). Do not use the signup server: it is for apps that implement an OAuth client (Journey D) and needs OAuth client values a chat agent cannot supply.",
          "humanRequired": false
        },
        {
          "id": "C2",
          "description": "The browser opens the Motorical sign-in page. The person creates the account there (onboarding), sets up MFA, which is required and cannot be skipped, and presses Allow on the consent screen. Consent resumes by itself; nobody runs authenticate a second time. No credential of any kind is returned to the agent.",
          "humanRequired": true,
          "sayToHuman": "A browser window opened. Create your Motorical account there, set up two-factor authentication (required for this connection), then press Allow. I never see or hold your credentials."
        },
        {
          "id": "C3",
          "description": "Continue as Journey B: the OAuth grant from consent is usable immediately, no separate step needed. The account starts on the unpaid developer sandbox (outbound-locked to an allowlist).",
          "humanRequired": false
        },
        {
          "id": "C4",
          "description": "To send in production, convert the sandbox Motor Block onto a domain the human owns. motorical_sandbox_convert requires an active Motorical plan — it throws subscription_required with err.data.upgradeUrl if none exists yet; retry the same call once the human has subscribed.",
          "tool": "motorical_sandbox_convert",
          "humanRequired": true,
          "sayToHuman": "To move off the sandbox onto your own domain, you'll need an active Motorical plan — here's the link to subscribe.",
          "stopCondition": "motorical_sandbox_convert fails closed with subscription_required until the human subscribes; there is no agent-side workaround."
        },
        {
          "id": "C5",
          "description": "Add the domain and complete DNS verification before any production send is possible.",
          "tools": [
            "motorical_domain_add",
            "motorical_domain_verify",
            "motorical_domain_check_dns"
          ],
          "humanRequired": true,
          "sayToHuman": "Add these DNS records at your domain registrar, then let me know once they're live so I can verify them.",
          "stopCondition": "A domain is not sendReady until DKIM and SPF are both configured — the human must publish the DNS records before send works."
        }
      ]
    },
    {
      "key": "sandbox_then_convert",
      "title": "Journey: sandbox then convert",
      "audience": "Agent that is only trying things, or whose person has no domain yet or is not at the keyboard (Path 1)",
      "status": "real",
      "route": "Hosted MCP over OAuth (main or sandbox server). Decision step in docs.motorical.com/ai, section \"Two ways to start\".",
      "steps": [
        {
          "id": "S1",
          "description": "Read live account state. For an account with no plan and no domain it lists the two onboarding options and a choose rule; state the choice and the reason, the person may override.",
          "tool": "motorical_get_onboarding_state",
          "humanRequired": false
        },
        {
          "id": "S2",
          "description": "Provision the sandbox on a Motorical-owned .sandbox domain. It is instant and needs no DNS. The Motor Block is outbound-locked and added to the live authorization.",
          "tool": "motorical_sandbox_provision",
          "humanRequired": false
        },
        {
          "id": "S3",
          "description": "Check motorical_sandbox_status. domain.sandbox true means the Motorical-owned sandbox domain. allowlist is the effective list of recipients a real send may reach.",
          "tool": "motorical_sandbox_status",
          "humanRequired": false
        },
        {
          "id": "S4",
          "description": "To send to anyone besides the account address, ask each recipient to confirm by emailed link. An authorization granted before the manage:recipients scope existed must be re-approved to include it.",
          "tool": "motorical_free_recipient_add",
          "humanRequired": true,
          "sayToHuman": "I have asked this person to allow test email from your account. They need to open the message and click the confirm button.",
          "stopCondition": "This tool never confirms for the recipient. Until they click, sends to that address are refused (sandbox_outbound_locked while locked, the default; sandbox_recipient_denied after an explicit unlock)."
        },
        {
          "id": "S5",
          "description": "Dry-run the send, then send for real to the account address or a confirmed recipient. The tool defaults to dryRun true and a real send needs confirmRealSend true; always set dryRun explicitly.",
          "tool": "motorical_send_email",
          "humanRequired": false
        },
        {
          "id": "S6",
          "description": "Wait for a terminal outcome instead of treating 202 as delivered. Pass the taskId from the send response's nextAction (it is not the message id). Without MCP, mint a read token with POST /api/public/token (scope logs.read) and call GET /api/public/v1/messages/{id}; the sandbox send key cannot read message status.",
          "tool": "motorical_wait_for_outcome",
          "humanRequired": false
        },
        {
          "id": "S7",
          "description": "To leave the sandbox, the person subscribes to a plan, then convert the sandbox Motor Block onto a verified domain they own. Without a plan the call fails closed with subscription_required and an upgradeUrl.",
          "tool": "motorical_sandbox_convert",
          "humanRequired": true,
          "sayToHuman": "To move off the sandbox onto your own domain you will need an active Motorical plan. Here is the link to subscribe."
        }
      ]
    },
    {
      "key": "free_own_domain",
      "title": "Journey: Free account with your own domain",
      "audience": "Agent whose person has a domain whose DNS they control and want to keep (Path 2)",
      "status": "real",
      "route": "Hosted MCP over OAuth (main server, or domains plus sandbox). Decision step in docs.motorical.com/ai, section \"Two ways to start\".",
      "steps": [
        {
          "id": "F1",
          "description": "Read live account state and state which path you chose and why.",
          "tool": "motorical_get_onboarding_state",
          "humanRequired": false
        },
        {
          "id": "F2",
          "description": "Add the domain. A Free account may have one domain; adding another returns free_domain_limit with a nextAction.",
          "tool": "motorical_domain_add",
          "humanRequired": false
        },
        {
          "id": "F3",
          "description": "Publish the DNS records exactly as returned, then re-check until DKIM and SPF pass.",
          "tools": [
            "motorical_domain_verify",
            "motorical_domain_check_dns"
          ],
          "humanRequired": true,
          "sayToHuman": "Add these DNS records at your domain registrar, then tell me once they are live so I can verify them.",
          "stopCondition": "The domain is not send-ready until DKIM and SPF are both configured; the person must publish the records."
        },
        {
          "id": "F4",
          "description": "Once DKIM and SPF pass, on a Free account when automatic provisioning is enabled and the domain is fully verified, a sandbox Motor Block is created on the domain automatically (no provisioning call). Check motorical_sandbox_status: domain.sandbox false means your own domain. If no block appeared (automatic provisioning off, or the account is not a Free account), motorical_sandbox_provision uses the Motorical-owned domain instead.",
          "tool": "motorical_sandbox_status",
          "humanRequired": false
        },
        {
          "id": "F5",
          "description": "If the account state reports grant_missing_sandbox, the current authorization predates the new Motor Block: the person must reconnect (re-authorize) the client.",
          "tool": "motorical_get_onboarding_state",
          "humanRequired": true,
          "sayToHuman": "Please reconnect this client in your connector settings so it can use the new sandbox Motor Block."
        },
        {
          "id": "F6",
          "description": "Ask each recipient to confirm by emailed link. An authorization granted before the manage:recipients scope existed must be re-approved to include it.",
          "tool": "motorical_free_recipient_add",
          "humanRequired": true,
          "sayToHuman": "I have asked this person to allow test email from your account. They need to open the message and click the confirm button."
        },
        {
          "id": "F7",
          "description": "Dry-run, send for real, and wait for a terminal outcome.",
          "tools": [
            "motorical_send_email",
            "motorical_wait_for_outcome"
          ],
          "humanRequired": false
        },
        {
          "id": "F8",
          "description": "To lift the sandbox, the person subscribes to a plan, then convert the sandbox Motor Block (confirm required).",
          "tool": "motorical_sandbox_convert",
          "humanRequired": true,
          "sayToHuman": "To lift the sandbox limits you will need an active Motorical plan. Here is the link to subscribe."
        }
      ]
    }
  ]
}
