{
  "id": "mango",
  "name": "Mango",
  "version": "0.1.0",
  "maturity": "contract-drafted-ui-prototype",
  "category": "marketing",
  "spine": {
    "id": "marketing",
    "record": "campaign over an audience"
  },
  "replaces": [
    { "name": "Mailchimp", "edition": null }
  ],
  "description": "An approval-first marketing desk that turns one ecommerce store's signals into evidence-backed email and SMS campaigns the owner reviews before anything is sent.",
  "scope": "One self-hosted deployment for one ecommerce store and its equal operators.",
  "capabilities": [
    {
      "id": "owner-front-door",
      "description": "A sessionless owner-identity landing at `/` that names the deployment owner and links to sign-in, with no vendor marketing content.",
      "clauses": ["HOME-001"]
    },
    {
      "id": "operator-access",
      "description": "Equal operators, invited and removed in settings, with every action on proposals, campaigns, and contacts attributed and retained after removal.",
      "clauses": ["OPER-001", "OPER-002", "OPER-003"]
    },
    {
      "id": "commerce-signal-ingestion",
      "description": "Imports customers, products, orders, refunds, carts, and browsing events from one connected commerce store, tracks per-source freshness, and blocks approval on stale or missing sources.",
      "clauses": ["STORE-001", "STORE-002", "STORE-003", "STORE-004", "STORE-005", "STORE-006", "STORE-007"]
    },
    {
      "id": "contact-and-consent-ledger",
      "description": "Tracks each contact's independent per-channel consent state, evidence, opt-outs, imports, merges, dormancy, and erasure with a surviving opt-out record.",
      "clauses": ["CONT-001", "CONT-002", "CONT-003", "CONT-004", "CONT-005", "CONT-006", "CONT-007", "CONT-008", "CONT-009", "CONT-010", "CONT-011", "CONT-012", "CONT-013"]
    },
    {
      "id": "evidence-backed-proposals",
      "description": "Generates ranked, evidence-cited campaign proposals from store data with grounded AI content and honest forecast provenance, held as drafts until an operator approves a revision.",
      "clauses": ["PROP-001", "PROP-002", "PROP-003", "PROP-004", "PROP-005", "PROP-006", "PROP-007", "PROP-008", "PROP-009"]
    },
    {
      "id": "audience-safety",
      "description": "Resolves and rechecks campaign audiences against consent, commerce, dormancy, and frequency rules, freezing the candidate set at approval and re-excluding at dispatch.",
      "clauses": ["AUD-001", "AUD-002", "AUD-003", "AUD-004", "AUD-005", "AUD-006", "AUD-007"]
    },
    {
      "id": "campaign-review-and-scheduling",
      "description": "Lets operators draft, preview, approve, reschedule, test-send, and cancel campaigns, with atomic approval checks and idempotent scheduling.",
      "clauses": ["CAMP-001", "CAMP-002", "CAMP-003", "CAMP-004", "CAMP-005", "CAMP-006", "CAMP-007", "CAMP-008", "CAMP-009", "CAMP-010", "CAMP-011"]
    },
    {
      "id": "email-and-sms-safety",
      "description": "Enforces sender identity, authentication, unsubscribe and opt-out mechanics, link and content checks, personalisation fallbacks, and sending-reputation limits before any send.",
      "clauses": ["MSG-001", "MSG-002", "MSG-003", "MSG-004", "MSG-005", "MSG-006", "MSG-007", "MSG-008", "MSG-009", "MSG-010", "MSG-011"]
    },
    {
      "id": "reliable-delivery",
      "description": "Hands recipient-steps to email and SMS providers idempotently, reconciles unknown outcomes, throttles and retries transport failures, and pauses a channel during a provider outage.",
      "clauses": ["SEND-001", "SEND-002", "SEND-003", "SEND-004", "SEND-005", "SEND-006", "SEND-007", "SEND-008", "SEND-009"]
    },
    {
      "id": "campaign-results",
      "description": "Reports campaign history, engagement, attribution, cost, tracked-link activity, and deliverability without presenting forecasts as facts.",
      "clauses": ["RESULT-001", "RESULT-002", "RESULT-003", "RESULT-004", "RESULT-005", "RESULT-006"]
    },
    {
      "id": "portable-data",
      "description": "Exports every contact, source record, consent record, campaign, and audit entry as JSON alongside uploaded campaign assets.",
      "clauses": ["DATA-001", "BASE-DATA-001"]
    }
  ],
  "nonGoals": [
    "Multiple stores or workspaces",
    "Roles and permissions",
    "A general CRM",
    "Signup-form, landing-page, and website builders",
    "Social and advertising campaigns",
    "Store transactional messaging (order, shipping, and account notifications)",
    "Autonomous sends",
    "Visual journey automation",
    "A/B and multivariate testing",
    "Predictive send-time models",
    "Multi-touch attribution",
    "Purchased-list prospecting",
    "Native mobile apps"
  ],
  "externals": [
    {
      "id": "commerce-store",
      "required": true,
      "why": "Mango needs the buyer's real customers, products, carts, browsing signals, orders, refunds, and inventory to identify and recheck opportunities.",
      "data": [
        "Webhook acknowledgements sent back to the store confirming receipt of an event",
        "Explicit adapter requests for source records (customers, products, orders, refunds, carts, browsing events)"
      ],
      "adapters": ["commerce.store.v1"]
    },
    {
      "id": "ai-provider",
      "required": true,
      "why": "A language model turns structured evidence into reviewable campaign content and explanations; Cloudflare primitives do not provide that model capability.",
      "data": [
        "The proposal evidence required for the requested generation",
        "The configured business context required for the requested generation"
      ],
      "adapters": ["ai.provider.v1"]
    },
    {
      "id": "mail-sender",
      "required": true,
      "why": "Internet email delivery, domain authentication, bounce handling, and complaint feedback require a delivery provider with established sending infrastructure.",
      "data": [
        "Approved email content",
        "Sender metadata",
        "Each eligible recipient's email address",
        "Operator email addresses that receive magic links, test sends, and dispatch-paused notices"
      ],
      "adapters": ["mail.sender.v1"]
    },
    {
      "id": "sms-sender",
      "required": true,
      "why": "Registered sending numbers, carrier routing, country approval, compliance keywords, and delivery receipts require a telecommunications provider.",
      "data": [
        "Approved SMS content",
        "Programme metadata",
        "Each eligible recipient's phone number"
      ],
      "adapters": ["sms.sender.v1"]
    }
  ],
  "env": [
    {
      "name": "SESSION_SIGNING_KEY",
      "required": true,
      "secret": true,
      "why": "Sign operator sessions and the opaque public tokens in unsubscribe, consent-confirmation, and tracked-link URLs."
    },
    {
      "name": "INITIAL_OPERATOR_EMAILS",
      "required": true,
      "secret": false,
      "why": "Comma-separated allow-list used to create the first operators during deterministic setup."
    },
    {
      "name": "CONTACT_HASH_SALT",
      "required": true,
      "secret": true,
      "why": "Salt the one-way opt-out hashes that survive contact erasure (CONT-013)."
    },
    {
      "name": "COMMERCE_STORE_URL",
      "required": true,
      "secret": false,
      "why": "Identify the single connected ecommerce store."
    },
    {
      "name": "COMMERCE_API_TOKEN",
      "required": true,
      "secret": true,
      "why": "Read configured commerce data through commerce.store.v1."
    },
    {
      "name": "COMMERCE_WEBHOOK_SECRET",
      "required": true,
      "secret": true,
      "why": "Verify commerce source events."
    },
    {
      "name": "AI_PROVIDER_API_KEY",
      "required": true,
      "secret": true,
      "why": "Generate grounded proposal explanations and message drafts through ai.provider.v1."
    },
    {
      "name": "MAIL_PROVIDER_API_KEY",
      "required": true,
      "secret": true,
      "why": "Send operator magic links, test emails, and approved marketing email through mail.sender.v1."
    },
    {
      "name": "MAIL_WEBHOOK_SECRET",
      "required": true,
      "secret": true,
      "why": "Verify email delivery, bounce, and complaint events."
    },
    {
      "name": "SMS_PROVIDER_API_KEY",
      "required": true,
      "secret": true,
      "why": "Send approved marketing SMS through sms.sender.v1."
    },
    {
      "name": "SMS_WEBHOOK_SECRET",
      "required": true,
      "secret": true,
      "why": "Verify SMS delivery and compliance replies."
    }
  ],
  "deploy": {
    "healthPath": "/health",
    "operatorPath": "/app",
    "publicPaths": [
      "/",
      "/health",
      "/signin",
      "/assets/",
      "/unsubscribe/",
      "/consent/",
      "/l/",
      "/webhooks/commerce/",
      "/webhooks/mail/",
      "/webhooks/sms/"
    ]
  },
  "customFields": ["contact", "campaign"],
  "extensionPoints": {
    "policies": [
      "operator.management.v1",
      "contact.opt-in.v1",
      "contact.soft-bounce.v1",
      "proposal.selection.v1",
      "audience.exclusions.v1",
      "audience.frequency.v1",
      "campaign.send-time.v1",
      "sms.quiet-hours.v1",
      "campaign.attribution.v1"
    ],
    "events": [
      "store.event-recorded.v1",
      "contact.consent-recorded.v1",
      "contact.channel-suppressed.v1",
      "contact.merged.v1",
      "proposal.created.v1",
      "proposal.failed.v1",
      "campaign.scheduled.v1",
      "campaign.dispatch-paused.v1",
      "campaign.cancelled.v1",
      "campaign.completed.v1",
      "delivery.outcome-recorded.v1"
    ],
    "slots": [
      "app.navigation.after.v1",
      "decision-desk.header.after.v1",
      "proposal.evidence.after.v1",
      "proposal.actions.before.v1",
      "campaign.review.summary.after.v1",
      "campaign.review.audience.after.v1",
      "campaign.review.safety.after.v1",
      "campaign.detail.tabs.after.v1",
      "contact.detail.tabs.after.v1",
      "settings.sections.after.v1"
    ],
    "adapters": [
      "commerce.store.v1",
      "ai.provider.v1",
      "mail.sender.v1",
      "sms.sender.v1"
    ],
    "contributions": [
      "routes.v1",
      "pages.v1",
      "navigation.v1",
      "settings.sections.v1",
      "jobs.v1",
      "cron.v1",
      "queue.consumers.v1"
    ]
  },
  "limits": {
    "status": "estimated-until-load-tested",
    "maxContacts": 100000,
    "maxStoredCommerceEvents": 5000000,
    "maxContactImportRows": 50000,
    "maxCampaignSteps": 10,
    "maxEligibleContactsPerCampaign": 100000,
    "maxCampaignAssetBytes": 10485760,
    "allowedCampaignAssetContentTypes": ["image/jpeg", "image/png", "image/gif", "image/webp"],
    "maxEmailHtmlBytes": 102400,
    "maxSmsSegmentsPerRecipientStep": 3,
    "emailHandoffsPerMinute": 3000,
    "smsHandoffsPerMinute": 600,
    "deliverabilityReportingWindowDays": 30,
    "operatorRequestsPerMinute": 300,
    "publicRequestsPerMinutePerIp": 60,
    "unsubscribeRequestsPerMinutePerToken": 10,
    "providerWebhooksPerMinute": 5000,
    "providerReconciliationIntervalMinutes": 15,
    "unresolvedProviderOutcomeHours": 24
  },
  "accessibility": {
    "level": "WCAG 2.2 AA",
    "status": "target-not-yet-verified"
  },
  "operatingCost": {
    "assumptions": "One store, 10000 contacts, 4 campaigns per month, 20000 email and 5000 SMS recipient-steps, and 500 AI generations. Queue messages are at most 64 KB and dispatch stays below 3334 recipient-steps per UTC day.",
    "cloudflareMonthlyUsd": "$0 on the current Workers Free plan while the deployment stays inside every Free quota: at most 100000 dynamic Worker requests per UTC day with 10 ms CPU per invocation; 10000 Queue operations per UTC day (write, read, and delete are each one operation per 64 KB); D1 at 5 million rows read per UTC day, 100000 rows written per UTC day, and 5 GB total storage; and SQLite Durable Objects at 100000 requests and 13000 GB-s per UTC day. Workers Paid starts at $5/month and is required for the declared 100000-contact campaign: its 100000 recipient-step writes plus step and schedule mutations exceed D1 Free's 100000 rows-written-per-UTC-day limit.",
    "externalMonthlyUsd": "Not yet estimated because the reference commerce, email, SMS, and AI adapters have not been selected; required before first release.",
    "totalMonthlyUsd": "Incomplete until reference adapters and unit prices are selected.",
    "status": "cloudflare-free-plan-assumptions-researched-2026-08-31; external-adapters-unselected"
  }
}
