Comal (comal.redbroomsoftware.com)
Multi-tenant e-commerce SaaS — store builder with subscription clubs, gated digital content, physical product shipping, gift cards, and store credit, all on a single platform.
URL: https://comal.redbroomsoftware.comStatus: LIVE Tier: T2 (vertical SaaS primitive)
T1 apps are indispensable horizontal primitives. T2 apps are vertical SaaS or single-domain primitives — primitive-like in their domain but not universal dependencies.
What you can integrate
- Order events — receive
order.completedandorder.refundedwhen a consumer checkout finalizes or a refund is issued - Customer creation —
customer.createdfires when a new buyer registers on a store, enabling Camino CRM enrichment - Product catalog sync —
product.updatedkeeps downstream inventory or search indexes current - Subscription club hooks — Comal manages recurring subscription orders; consume
catalog.sync.completed/catalog.sync.failedfor Camino-driven catalog pushes - Fiscal integration — receives
cfdi.stamped/cfdi.failedfrom Constanza andwallet.payout_completedfrom Colectiva for merchant earnings settlement
Authentication
Authorization: Bearer <api_key>
Content-Type: application/jsonUser identity flows through Camino SSO. Per-store API keys are issued from the Comal merchant console. Consumer-facing checkout uses session tokens, not API keys.
Endpoints
| Method | Path |
|---|---|
| GET | /api/address/lookup |
| GET | /api/catalog/:slug |
| POST | /api/content/:id |
| POST | /api/content/purchase |
| POST | /api/content/stream-upload-url |
| POST | /api/content/upload-url |
| POST | /api/discounts/validate |
| POST | /api/ecosystem/upsell-track |
| GET, POST | /api/ecosystem/webhooks |
| GET | /api/gift-cards/denominations |
40 total — 9 admin/cron/internal (hidden) — +21 more primary endpoints not shown. Source: src/routes/api/**/+server.{ts,js} in the comal repo.
Events emitted
| Event | Receivers | Description |
|---|---|---|
app.tenant_id_assigned | (none declared) | An app has bound its local tenant identifier to a canonical RBS org for the first time (or backfilled an existing binding). NOT routed via the standard webhook bus — producer calls CPR POST /api/v1/internal/tenant-mapping/upsert directly with X-RBS-Auth: App HMAC. Receiver list is empty because no app-side handler exists; the consumer is the Control Plane (ecosystem.redbroomsoftware.com). Topic 25 Phase 3 substrate — populates app_tenant_mappings junction. Senders list will grow as each app's producer wires (caracol first per S381 Phase 1 Session 3). |
customer.created | camino, constanza, garita | A new customer/contact/third-party record was created — used for CRM sync, fiscal pre-registration, and cross-app contact linking. |
inventory.low | camino, colectiva | A product inventory fell below the low-stock threshold — Colectiva records the alert for analytics; Camino logs it as a TenantIntelligence signal in organization_activity_log. |
order.completed | colectiva, constanza | An e-commerce order has been completed (paid + fulfilled) |
order.refunded | colectiva, constanza | An e-commerce order has been refunded |
payment.refunded | baul, mancha, servilleta, agora, constanza, cosmos-pet, caracol | A payment was refunded by Colectiva — voluntary refund initiated by a tenant app or by Colectiva ops, OR an involuntary MP-platform-side refund. Receivers branch on paymentReferenceType. For voluntary refunds with proration, isPartial=true and proration block carries period bounds. paymentLayer is the primary routing discriminator; receivers MUST check it before paymentReferenceType. |
product.created | camino | A new product was created in Comal — Camino syncs to the CRM product catalog for cross-channel posting (e.g., TikTok catalog, social feeds). |
product.deleted | camino | A Comal product was deleted — Camino removes from cross-channel catalog mirrors. |
product.updated | camino | [auto-derived] product.updated event |
Events consumed
| Event | Senders | Description |
|---|---|---|
catalog.sync.completed | camino | Camino → Comal callback when a TikTok catalog sync succeeds for a Comal store product. Comal updates products.tiktok_product_id + tiktok_synced_at. |
catalog.sync.failed | camino | Camino → Comal callback when a TikTok catalog sync fails. Comal surfaces the error to the merchant in the product list. |
cfdi.failed | constanza | A CFDI invoice stamping has failed |
cfdi.stamped | constanza | A CFDI invoice has been stamped by the SAT via Constanza — receiver apps attach the UUID to their local invoice record and update status. |
invoice.compliance_issue | constanza | Constanza detected an invoice compliance issue (missing/invalid RFC, duplicate, format error) — routed back to the source app for review. |
subscription.activated | camino, colectiva | A subscription plan has been activated for a tenant — all apps that gate features behind the subscription tier update their local tier state. |
subscription.cancelled | camino, colectiva | A subscription has been cancelled — all apps that gate features behind the subscription tier update their local tier state to free/cancelled. |
subscription.changed | camino | A subscription plan has been upgraded or downgraded — receiver apps update their tier gates and feature access accordingly. |
subscription.expired | camino | A subscription has expired (end of billing period, no renewal) |
subscription.past_due | camino | A tenant's subscription entered past_due state after a failed renewal — Camino dunning flow pauses feature access without cancelling. Receiver apps mark memberships or subscriptions past_due until payment is received. |
subscription.trial_expiring | camino | A trial subscription is expiring soon — receiver apps surface an upgrade prompt to the tenant operator. |
subscription.trial_started | camino | A trial subscription was started — receiver apps activate trial-tier features for the new tenant. |
wallet.payout_completed | colectiva | [auto-derived] wallet.payout_completed event |
Webhook signature
Standard ecosystem HMAC-SHA256 over the raw body with a 5-minute timestamp window. See Webhooks.
OpenAPI
Coming in the OpenAPI generator phase — see /reference/openapi.
SDK
See /sdk/ for @r-bsoftware/ecosystem-sdk helpers.
Gotchas
- Comal stores are isolated by
comal_store_{id}. An API key is scoped to a single store — there is no cross-store query surface. - Subscription club orders emit
order.completedlike a standard one-time order. Differentiate recurring from one-time purchases using thesubscriptionIdfield in the payload. - CFDI invoice requests (
/api/invoices/request) are async — they emitinvoice.requested, Constanza stamps, andcfdi.stampedfires back. Do not poll the payment endpoint for fiscal status.