Documentation

FloorSync for agents

Setting up as an agent? Read /agent-install.md: the whole setup in plain HTTP calls, dry run first, no installs.

Try it now, no key

An agent can POST any Stripe event to /api/v1/demo/events/stripe with no key (a few calls an hour). It gets back what Meta and GA4 would get, the ledger line, and a short note for its owner with what’s needed before live sends and the sign-up link. Nothing is sent or kept. On MCP, it’s the try_stripe_event tool.

Machine-readable: /llms.txt, /openapi.json, /api/capabilities and the MCP endpoint POST /api/mcp.

1. The owner sets it up

  1. The floor owner creates an account and gets the floor key (fs_…).
  2. They save the Meta pixel ID with a Conversions API access token, and the GA4 Measurement ID with a Measurement Protocol API secret.
  3. They start the plan: $10 a month per floor, 7 days free. Agents can’t buy it; until it starts, live calls answer 402 and dry runs still work.

2. The agent forwards each Stripe event

In the floor’s /api/stripe-events route, after handling the event. Every POST sends the floor key and the body’s SHA-256 (the CDN asks for it).

import { createHash } from 'node:crypto'

const text = JSON.stringify(event) // exactly as received
await fetch('https://floorsync.towers.app/api/v1/events/stripe', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'X-Floor-Key': FLOORSYNC_KEY,
    'x-amz-content-sha256': createHash('sha256').update(text).digest('hex'),
  },
  body: text,
  signal: AbortSignal.timeout(10000),
}).catch(() => {})

Add ?dry_run=1 to get the exact Meta and GA4 payloads back, with GA4’s validation answer, and send nothing.

3. What goes where

Stripe eventMetaGA4
checkout.session.completed (paid)Purchase, event_id = session idpurchase, transaction_id = session id
checkout.session.completed ($0 trial)StartTrialstart_trial
invoice.paid (subscription_cycle)Purchase, event_id = invoice id, system_generatedpurchase with the stored client_id
invoice.payment_failed, refunds, subscription deletedLedger only

Each event id counts once. Email and phone go to Meta SHA-256 hashed only; GA4 gets no personal data. A failed send is kept in the sync log and retried each hour, 3 tries in all. A floor’s first GA4 send is checked on GA4’s validation server first.

4. Attribution at checkout (recommended)

When the floor creates a Checkout session, post the buyer’s ids so the sale and its renewals match the right visitor:

POST /api/v1/attribution
{"checkout_session_id": "cs_…", "fbp": "fb.1.…", "fbc": "fb.1.…", "client_id": "1234567890.1234567890",
 "ip": "203.0.113.7", "user_agent": "Mozilla/5.0 …", "page_url": "https://yourfloor.towers.app/pricing/"}

5. Read the ledger

GET /api/v1/ledger?days=7
{"mode": "live", "days": 7, "currency": "USD", "revenue": 10,
 "counts": {"new_paid": 1, "trials": 0, "renewals": 0, "refunds": 0, "failed_payments": 0, "cancellations": 0},
 "conversions": {"meta": {"delivered": 1, "failed": 0, "skipped": 0}, "ga4": {"delivered": 1, "failed": 0, "skipped": 0}},
 "spend": null, "spend_source": null, "revenue_per_spend": null,
 "plan_active": true, "health": [], "note": "Nothing to fix."}

An example answer. Spend comes from Meta Insights when the owner saves an ads_read token, or from POST /api/v1/spend.

Errors