Skip to content New SourceLoop MCP: chat with your attribution data in Claude, ChatGPT & Cursor
SourceLoop
API

Data provenance and PII

Where each field came from, why that matters before you automate on it, and how personal data is masked when a key lacks permission to read it.

Two properties of this API decide whether an automation you build on it is safe. Both are unusual enough to be worth a page.

Not every field came from the same place

A response mixes three kinds of field, and they carry different authority:

KindWhere it came fromSafe to act on
CapturedObserved by SourceLoop directly: the visit, the referrer, the campaign, the form submission, the touch sequenceYes. This is our own first-hand record
MirroredCopied from a system you own, usually your CRM: deal stage, owner, company size, lifecycle statusWith care. It is a copy, and it is only as fresh as synced_at
DerivedComputed by us from the two above: attributed revenue, cost per lead, model-weighted creditYes, but the number depends on the model and window you asked for

Mirrored fields carry a synced_at timestamp. Check it before you write back. An automation that reads a stale mirror and pushes a “correction” into the system that actually owns the field will fight that system, and the loser is usually your data.

Fields you can write are documented as writable on the endpoint that exposes them. A deal value written through PATCH /v1/deals/{id} is applied to the CRM that owns the deal, not just to our copy, which is why it needs deals:write rather than a local scope.

Absent is not zero

Where a number is unavailable, it comes back as null, not 0. This is deliberate and it is the mistake most likely to poison an automated report.

“No ad account connected” is not “you spent nothing”. “This contact has no deal” is not “this deal is worth zero”. A dashboard that renders null as 0 will quietly show a cost per lead of zero and a founder will make a budget decision on it.

Every response also carries a definitions block explaining what each metric counts, so a figure from this API is never ambiguous about which day boundary or which model produced it.

Personal data follows the key

Email addresses, phone numbers and names are returned masked unless the calling key carries pii:read:

{ "email": "j•••@acme.com", "phone": null, "name": "J. D." }

Masking keeps the email domain, so a masked row still tells you the lead was at acme.com. The response says this has happened rather than leaving you to infer it, through meta.pii_included and a pii_note.

New keys are created with pii:read included, so this is a decision you make when you mint the key, not a protection you get for free. Keep it for integrations that genuinely need identity, such as syncing contacts into a CRM or sending a receipt. Remove it for everything else.

Everything else is a longer list than it first looks: reporting jobs, spreadsheet exports, BI syncs, and above all anything connected to an AI assistant. A key handed to the MCP server sends its responses to a third-party model provider, which may retain them, so that is the case where a key without pii:read is worth the small inconvenience.

Searching by email or phone counts as reading it, so those filters need the scope too.

Two consequences to design around:

  • Masked values are not stable identifiers. Do not use them as a key to join or dedupe on. Use the contact id.
  • Turning the scope on changes the response, not just what you can see. Test your parser against both shapes if the same code path serves keys of both kinds.

Deleting data

Deletion is a support operation rather than an API call, because a delete has to span several stores that do not cascade, and a partial delete is worse than none. Contact us with the record ids and we will handle it as one operation.

Track every conversion to its true source

Capture and send full attribution data from every signup, lead, booking, and sale to your CRM and ad platforms, so you know exactly what's driving revenue.

Without SourceLoop

Untagged

Kayden Floyd

kayden@abc.com

  • SourceUnknown
  • MediumUnknown
  • CampaignUnknown
  • Landing pageUnknown
Journey
No touchpoints captured

With SourceLoop

Auto-tagged

Kayden Floyd

kayden@abc.com · Acme Co.

  • Channel Paid Social
  • CampaignFree_demo
  • Landing page/pricing
Journey
Synced to HubSpot Google Ads Meta