Platform integration
Security and data
- CategoryPlatform
Overview
MessageMind treats customer data as something to minimise, scope and prove, not store defensively. Provider credentials (API keys, OAuth tokens, webhook secrets) are encrypted at rest with a versioned cipher before they ever land in the database, so an operator reading the collection sees opaque 'v1.' blobs rather than live secrets. Inbound webhooks are authenticated against per-integration HMAC signatures using timing-safe comparison, OAuth 2.0 flows carry an HMAC-signed state value scoped to a single callback, and personal data lives behind a dedicated 'user' key that the analytics layer hashes to SHA-256 before anything leaves the browser or the server. Public configuration is deliberately boring: a concrete HTTPS base URL, a few 'PUBLIC_*' env vars, and a server that fails closed when any of them is missing.
What MessageMind can do with it
- Provider credentials (OAuth refresh tokens, API keys, webhook signing secrets) are encrypted at rest with a versioned 'v1.' cipher. The decrypted value never leaves the server process.
- Every integration that supports a signed webhook verifies HMAC-SHA256 of the raw body (base64 or hex per vendor contract) against a per-integration secret before any state is written.
- Signature comparison uses 'crypto.timingSafeEqual' on equal-length buffers, so the compare step cannot leak secret bytes through response-time differences.
- OAuth redirects (Salesforce, HubSpot and the other OAuth 2.0 integrations) ride an HMAC-signed, time-bounded state value with a 10-minute TTL, protecting the callback against CSRF and replay.
- Personal data flows under a dedicated 'user' key that the analytics layer routes to identify(). It is normalised and hashed to SHA-256 synchronously before any ad-platform call, and the raw value is never attached to vendor custom data.
- Server-side ad-platform relays (Meta Conversions API Lead and Schedule) reuse the browser-minted event id so Meta dedups the server twin against the pixel; the server keeps no event store of its own.
- Inbound webhook URLs that carry a per-account token in the path use the 'userId.48-hex-secret' shape, where only the id half routes and only the secret half authenticates (compared in constant time).
Requirements
- A concrete HTTPS public base URL for every server surface the website calls (lead intake, booking proxy, event relay). The build fails closed when any of them is missing, interpolated, or carries credentials, a query or a fragment.
Available data and actions
Reads
- Provider credentials (OAuth tokens, API keys, webhook signing secrets) are read decrypted into memory only on the request that needs them, then dropped.
- Personal data on contacts and leads under the 'user' key: name, email, phone, and the UTM/fbc/fbp identifiers the browser already carries.
- Inbound webhook signatures from each integration (X-Hub-Signature-256, dengro-signature, x-unleashed-signature, x-be-signature, X-Beatrice-Secret / X-Hercules-Secret, messagemind_source + messagemind_origin + HMAC on WooCommerce order echoes).
Writes
- SHA-256 hashed identity (email, phone, first name, last name) to Meta via the Conversions API 'Lead' and 'Schedule' twins fired from the lead intake and the booking proxy.
- Server-side event relays for every browser event that has no dedicated server twin (page views, pricing views, CTA / sign-up / contact clicks, demo funnel steps, partner leads), carrying the EVENT key, the browser-minted event id and the pre-hashed identity. The server fires the Conversions API twin and keeps nothing.
AI agent use cases
- Compliance review: walk a prospective buyer through where credentials sit, how they are encrypted, and which headers each inbound webhook is authenticated by.
- Data audit: list every field read from an integration and every field written to an ad platform, so legal can confirm the lawful basis and the data minimisation story.
- PII removal request: delete a contact's 'user' values; since the server stores no first-party lead inbox, the only remaining copies are inside the integrations the operator owns (CRM, calendar, inventory) and are deleted there.
- Credential rotation: regenerate a provider secret in the vendor dashboard, paste the new value into the MessageMind card, and the next encrypt-at-rest write replaces the old 'v1.' blob without downtime.
Configuration
- Credentials are sealed with the server's provider-credential cipher. The boot sequence fails fast in production when the key is not configured, so an unsealed credential cannot be written.
- Signed webhooks carry their HMAC in a vendor-specific header: 'X-Hub-Signature-256' (Meta family), 'dengro-signature' (DenGro), 'x-unleashed-signature' with 'x-unleashed-timestamp' (Unleashed), 'x-be-signature' (Booking Experts), 'X-Beatrice-Secret' or 'X-Hercules-Secret' (Hercules). Vendors with a timestamp header enforce a ±5 minute skew window to bound replay.
- OAuth state values are HMAC-signed with the server's OAUTH_STATE_SECRET and expire after 10 minutes; a callback whose state is expired, tampered with or replayed is rejected before the token exchange.
- Per-account inbound tokens are a 48-hex-character secret appended to the account id. Only the secret half authenticates, and it is compared in constant time against the stored value.
- The Facebook identity latch locks only on real PII, so an anonymous page view can never attach the previous visitor's hashed identity to a new session.
Example workflows
How an inbound webhook is authenticated
- The vendor POSTs the event to the per-integration MessageMind URL, carrying the signature in its own header (for example 'x-unleashed-signature' or 'dengro-signature') and, where supported, a timestamp header.
- MessageMind reads the raw request body (not the parsed JSON) so the signature is computed over the exact bytes the vendor signed.
- The server loads the per-integration signing secret, decrypts it from the 'v1.' blob into memory, and recomputes HMAC-SHA256 of the raw body (prefixed with the timestamp where the vendor contract requires it).
- The computed digest and the header digest are converted to buffers of equal length and compared with 'crypto.timingSafeEqual'. A mismatch, or buffers of different lengths, rejects the delivery before any downstream write.
- If the vendor sends a timestamp header, a ±5 minute skew window is enforced on it so a captured replay outside that window is dropped even when the signature is otherwise valid.
- Only after both checks pass does the handler decode the body, take its entity-ledger claim and run the automation trigger.
Limitations
- The research surfaces no public compliance certifications (no SOC 2, ISO 27001, HIPAA or PCI DSS attestations in this repo). Any claim of a specific certification should come from the security team, not from this page.
- By design there is no first-party lead store on the website server. Leads are automation triggers and ad-platform signals, not a readable inbox; a buyer who expects 'export every lead that ever hit the site' from MessageMind alone will not find one.
- Only integrations whose vendor supports a signed webhook can enforce HMAC verification. A handful of vendors ship an unsigned inbound webhook authenticated purely by the per-account URL token (the custom webhook, Perspective); those are documented honestly on their own pages.
- Encryption at rest protects credentials inside the MessageMind database. A copy of the same secret still sits inside the vendor's own dashboard, where its security posture is the vendor's responsibility.
Troubleshooting
An inbound webhook returns 401 'invalid signature' even though the secret looks right.
Most signature checks are computed over the raw request body. Confirm the sender is signing the exact bytes it POSTs (no pretty-printing, no re-serialising) and that the signing secret pasted into the vendor matches the one shown in the MessageMind integration card byte for byte.
An OAuth callback fails with 'state expired' or 'state invalid'.
The HMAC-signed state value has a 10-minute TTL. Restart the connect flow from the MessageMind card so a fresh state is minted; do not reload a stale callback URL from a previous attempt.
A webhook with a correct signature is still rejected.
Check the clock skew. Vendors that sign '{timestamp}.{body}' (Unleashed, and others that follow the same pattern) enforce a ±5 minute window on their timestamp header; a sender whose clock has drifted will fail verification even with a valid HMAC.