A Customer 360 identity layer on Xano — stitch Segment events, Snowflake attributes, and Salesforce CRM into one consent-masked profile served through a single API and MCP tool.
Start building in Xano: Access the prompt at https://go.xano.co/start-xano-with-template-skill and run it here with customer-360-segment-snowflake-salesforce as your template.Copy and paste the prompt in your local coding agent of choice.
Template Details
Integrations
A Customer 360 identity layer on Xano: stitch anonymous and known identifiers into one customer, merge behavioral events (Segment), warehouse attributes (Snowflake), and CRM records (Salesforce) into a single profile, and serve it through one API with per-field consent masking and a full audit log.
Product, support, and sales each see a different customer because the data lives in three systems: events in Segment, revenue and firmographics in Salesforce, warehouse-modeled attributes in Snowflake. This template makes Xano the place those sources are joined — the identity graph, the merged-attribute cache, and the consent state live in Xano's native database, so every downstream consumer (a support console, in-app personalization, a sales workflow, or an AI agent) resolves the same customer with the same consent rules.
When customer data is scattered across a CDP, a warehouse, and a CRM, every team builds its own half-right join. Identity resolution gets re-implemented in each tool, "who is this customer" disagrees between systems, and consent — which fields a customer has actually allowed you to use, and for what — is enforced inconsistently if at all. The result is duplicated logic, contradictory profiles, and privacy risk.
This template centralizes that join. It keeps a native identity graph (anonymous_id / user_id / email aliases resolving to one identity), a merged-attribute cache with a deterministic source-precedence policy, and per-purpose consent that is enforced at read time so no consumer ever receives a field the customer hasn't allowed. The read path is self-contained and served entirely from Xano; Snowflake and Salesforce are pulled in on the write/enrich path to refresh the cache. It gives you the mechanisms that support a SOC 2 / GDPR / CCPA program — consent capture and withdrawal, per-field masking, and an immutable audit trail of every access — without pretending to be a compliance certification.
backend/
ai/
mcp_server/
customer_360.xs
tool/
lookup_customer_context.xs
api/
customer_360/
api_group.xs
customers_by_id_get.xs
customers_consent_get.xs
customers_consent_post.xs
customers_get.xs
customers_timeline_get.xs
customers_traits_get.xs
events_segment_post.xs
seed_post.xs
sync_snowflake_post.xs
function/
c360_apply_consent.xs
c360_assemble_360.xs
c360_auth.xs
c360_ingest_event.xs
c360_link_alias.xs
c360_query_salesforce.xs
c360_query_snowflake.xs
c360_resolve_identity.xs
c360_salesforce_delta_sync.xs
c360_salesforce_refresh.xs
c360_upsert_attribute.xs
c360_write_audit.xs
table/
attribute.xs
audit_log.xs
consent.xs
consumer.xs
event.xs
field_policy.xs
identity_alias.xs
identity.xs
sync_state.xs
task/
c360_nightly_salesforce_sync.xs
workflow_test/
c360_auth_rejects_invalid_key.xs
c360_masks_denied_analytics_fields.xs
c360_segment_ingest_resolves_and_dedups.xs
workspace/
customer_360.xs
Three planes, joined in Xano:
identity_alias edge (anonymous_id, user_id, or email) pointing at one identity. c360_resolve_identity stitches them with a fixed precedence (user_id → email → anonymous_id): it finds the existing identity for any provided identifier, creates one only when none matches, and links the remaining aliases. Merging two already-separate identities is deliberately not automatic — it's a destructive operation left to an explicit, audited step.attribute table (segment / snowflake / salesforce / manual). c360_assemble_360 merges them with a deterministic precedence — manual > salesforce > snowflake > segment — so the resolved value for any key is well-defined and every consumer agrees.field_policy maps each attribute key to a consent purpose and a sensitivity. c360_apply_consent checks the customer's consent for that purpose on every read; anything not granted is withheld (value nulled, key listed in masked_keys). Masking is a function, not an addon — Xano addons only attach related data, they can't transform it.The read path (GET /customers/*, the MCP tool) is served entirely from these native tables, so it runs and is tested without any third-party credentials. The enrich path refreshes the cache from outside:
POST /events/segment accepts identify/track/page/group events, deduplicates on messageId (at-least-once delivery), resolves the identity, stores the event for the timeline, and promotes identify traits to segment-sourced attributes.c360_query_snowflake calls the Snowflake SQL API v2 (POST /api/v2/statements, PAT auth, parameterized bindings) for LTV, 30-day product usage, and cohort, and upserts them as snowflake-sourced attributes.c360_salesforce_refresh runs the OAuth2 refresh-token flow; c360_query_salesforce runs SOQL against Contact/Account; a nightly task (c360_nightly_salesforce_sync) delta-syncs Contacts modified since a stored SystemModstamp cursor.Every read writes an audit_log row (who accessed which customer, and which fields were masked).
Start with your coding agent. Paste the prompt below into a local MCP-capable coding agent — Claude Code, Cursor, Windsurf, and others. It installs the Xano CLI, connects Xano, and imports this template.
Start building in Xano: Access the prompt at https://go.xano.co/start-xano-with-template-skill and run it here with customer-360-segment-snowflake-salesforce as your template.
The agent will walk you through the setup below.
backend/ export to a Xano workspace (the CLI/agent flow below does this for you).POST /api:c360-customer-360/seed — idempotent; creates 6 customers with aliases, seeded Segment/Snowflake/Salesforce attributes, mixed consent, field policies, API consumers, and events. No credentials needed.frontend/index.html, enter your instance base URL and the demo key c360_demo_support_key, and click Load demo data. Search a customer, open their 360, and flip a consent purpose to watch masking change live.POST /events/segment.The read path and Segment ingest run with no external credentials. Snowflake and Salesforce enrichment require your own accounts — see Environment variables.
All endpoints live in the customer_360 API group (canonical c360-customer-360). Consumer endpoints authenticate with a consumer key passed as api_key (query or body); see the note below.
Read (consumer-authenticated):
| Method | Path | Purpose |
|---|---|---|
GET |
/customers |
Search / list identities by email or display name (pass ?q=). A collection route is used instead of /customers/search because Xano routing is non-cascading and a {customer_id} wildcard would collide with a search literal at the same segment. |
GET |
/customers/{customer_id} |
The consent-masked 360: resolved identity + merged attributes + masked_keys. |
GET |
/customers/{customer_id}/timeline |
The event stream, newest first. |
GET |
/customers/{customer_id}/traits |
Just the masked trait key/values. |
GET |
/customers/{customer_id}/consent |
Current per-purpose consent state. |
Write:
| Method | Path | Purpose |
|---|---|---|
POST |
/customers/{customer_id}/consent |
Record/update consent for a purpose (granted / denied). Upserts and audits — the GDPR/CCPA capture-and-withdrawal mechanism. |
POST |
/events/segment |
The Segment webhook sink (see the HMAC note under Environment variables). |
POST |
/sync/snowflake |
Manually refresh one customer's warehouse attributes (requires Snowflake credentials). |
POST |
/seed |
Load the demo dataset (no auth). |
MCP server (c360-mcp): exposes one tool, lookup_customer_context, that returns a customer's consent-masked 360 and recent events by customer_id, email, or user_id — for support copilots and in-app assistants. It is read-only and never creates or mutates an identity.
Auth note. Consumers pass their key as the
api_keyparameter. A production hardening step is to move this to anX-API-Keyheader enforced in middleware; the template uses a parameter so the demo frontend and tests work without header plumbing.
anonymous_id / user_id / email value linked to an identity (unique per kind+value).message_id for dedup); powers the timeline.SystemModstamp delta).Run from a deployed workspace:
xano unit_test run_all) — the Snowflake and Salesforce legs, validated against mocks built from each vendor's own API documentation (the SQL API v2 statement response; the OAuth token + SOQL query response). These prove the request shape, auth headers, status handling, and response parsing against the documented contract — not against a live account.xano workflow_test run_all) — end-to-end on the seed data, credential-free:c360_masks_denied_analytics_fields — a customer with analytics consent denied gets ltv / cohort / product_logins_30d withheld; a fully-consented customer gets nothing masked.c360_segment_ingest_resolves_and_dedups — a new Segment identify event resolves an identity and lands on the timeline; a replay of the same messageId is deduplicated.c360_auth_rejects_invalid_key — unknown and revoked (inactive) consumer keys are rejected.What is and isn't proven for real: the identity graph, merge, consent masking, audit, and Segment ingest are exercised end-to-end against shipped seed data. The Snowflake and Salesforce enrich calls are proven against their documented contracts via mocks; running them against live data requires your own credentials (below). The nightly Salesforce task is wired and safe to leave enabled — without credentials the external call fails and is caught/logged as a no-op.
Set these to enable the outbound enrich path. The read path and Segment ingest need none of them.
Snowflake (SQL API v2). Requires a programmatic access token, and the token's user must have a network policy that allows your Xano instance's egress IPs. Large results return 202 and must be polled — this template reads the first synchronous page.
SNOWFLAKE_ACCOUNT_HOST — https://<account>.snowflakecomputing.com.SNOWFLAKE_PAT — the programmatic access token (sent as a Bearer token).SNOWFLAKE_DATABASE, SNOWFLAKE_SCHEMA, SNOWFLAKE_WAREHOUSE, SNOWFLAKE_ROLE — the query context.Salesforce (REST + SOQL). Requires an API-enabled edition (Enterprise/Unlimited/Developer — base Essentials/Professional/Group editions have no API access) and a Connected App with the api and refresh_token scopes; mint the refresh token once via an interactive OAuth flow.
SALESFORCE_LOGIN_URL — https://login.salesforce.com (or your My Domain / https://test.salesforce.com for sandboxes).SALESFORCE_CLIENT_ID, SALESFORCE_CLIENT_SECRET — the Connected App consumer key/secret.SALESFORCE_REFRESH_TOKEN — the stored refresh token.SALESFORCE_API_VERSION — optional, defaults to v67.0.Segment.
SEGMENT_WEBHOOK_SECRET — optional shared secret that gates POST /events/segment. When it is set, the endpoint rejects any request whose ?secret= query parameter doesn't match; when it is unset (e.g. the demo workspace) ingest is open. To use it, append ?secret=<value> to the webhook URL you configure in Segment.
The query-string gate is a basic deterrent, not production-grade auth. Because the secret travels in the URL, it can be captured in server access logs, proxies, and browser history — HTTPS encrypts the transport, but the full URL is still logged at each hop. Don't rely on it alone for a real deployment. Harden it one of two ways (both need a small Xano middleware to read the request header / raw body, which this template does not ship):
X-Webhook-Secret) instead of the query string, so it never appears in a URL; orX-Signature) computed over the raw request body — the strongest option, and the one Segment recommends.Xano gives you everything you need to ship modern applications—fast, securely, and at scale.
Get started