Read Identifiers & Scopes first. This page assumes
you know what
customer_id and user_id do.What a level is
A scope is a position on a ladder. Your account is the top rung, and each rung below it narrows who a memory belongs to. The default ladder has three:One ladder per account
You have one ladder, and every instance you run shares it. There is no per-instance ladder. This follows from what an id means. A customer id identifies the same customer across your whole account, and a user id the same person, whichever instance sent the request. Two instances writing about customeracme are writing about
one customer. If they disagreed about what the levels are, the same records
would carry different level names depending on which instance wrote them, and a
read from one would not find what the other stored.
Instances keep their own memory settings. The switch that turns nested scoping
on is also per instance, so you can enable it for one instance and watch it
before enabling the rest. The shape is shared; the moment each instance starts
using it is not.
If your account is B2C
Some accounts are set up so that a customer and a person are the same thing: one consumer, using your product for themselves, belonging to no organisation. Synap calls thisuser_context_isolation: equals_customer, and your integration sends
a user_id and no customer_id.
On those accounts the customer rung and the user rung are one node. Not two
levels that happen to have one member each. The same node, reached by either
name:
user_id. Sending a customer_id to
a B2C account is refused with customer_id_not_accepted_on_b2c, and a scope path
does not change that. If you send a path, name the customer rung with the same
value as the user rung.
Where your ladder comes from
You do not start from a blank ladder, and you do not start from ours either. When you onboard, you give us a use-case document: what your agent does, who it talks to, and what it should remember. That document already answers the scoping question, usually in your own words. A fintech client wrote “Client: the company operating the platform. Customer: an Indian retail consumer.” A clinical client wrote “memory is about the patient, not the clinician using the application,” which is a different shape entirely and says so plainly. So we read it and propose a ladder from it: how many rungs, what each one is called, and one sentence per rung explaining what it holds. The proposal quotes the line in your document that each rung came from, so you can see the reasoning rather than take it on trust.The proposal is a draft. It is not applied, and nothing about how your memories
are stored or read changes until you accept it. See Ratifying
it.
What we will and will not infer
We name rungs from what you wrote. We do not invent privacy boundaries from prose that does not ask for one.
A rung decides who can read whose memories. Adding one that you did not ask for
would be a privacy decision made by inference, so a rung we are not confident
about is raised as a question in the proposal rather than added to it.
Ratifying it
A proposed ladder does nothing. It is a draft until a person accepts it, and until then your account keeps using the default chain. Accepting records who accepted and when. That record matters because a ladder is close to permanent: a rung can be renamed at any time and can never be deleted, only retired, because memories already resolve through it. Before you accept, you can:- rename any rung, which is free and changes nothing about who reads what
- remove a proposed rung you do not want
- add one we did not propose
Which direction a read travels
This is the rule worth reading twice, because it is easy to hold backwards. A request sees its own rung and every rung above it. It never sees a rung below, and never a neighbour. So on the default ladder:
A memory filed at the user level is not visible to a request that identifies
only the customer. One person’s private context does not surface for their
colleague, and it does not surface for a customer-wide question.
Nothing sits below the person
The rung that identifies a person is always the last one. You cannot add a rung under it, and Synap refuses a ladder that tries. Two reasons, and both are hard to undo later. A rung under a person multiplies the work of consolidating memories against the busiest part of your tree, and it makes deletion ambiguous: erasing a person would first have to decide what happens to everything filed underneath them. The account rung at the top is fixed in the same way. It is your account itself, so nothing can go above it. Every rung you add goes somewhere in between.Ranking: closer material wins
Filtering decides what a request is allowed to read. Ranking decides what it sees first. Among the memories a request may read, the ones filed closer to the person asking rank above the ones filed further up. A person’s own preference outranks a fact that is true of their whole organization, when both match the question equally well. This is a ranking adjustment only: it never makes a memory readable that was not, and it never hides one that was.A level’s name can change, its key cannot
Every level has two parts.- The label is what people read: “Customer”, “Practice”, “Region”. Change it whenever you like. Nothing stored depends on it.
- The key is what stored memories resolve through:
customer,user. It never changes, because every memory already filed under it would stop resolving.
Sending a scope path
Requests normally identify a scope withcustomer_id and user_id, and Synap
maps those onto the default three rungs. That is what almost every integration
does, and it needs no change.
Why two ids only ever reach three rungs. They are matched by ROLE, not by
position: customer_id goes to whichever rung holds tenant_of_record, and
user_id to whichever holds identity. There are exactly three reserved roles,
so two ids can address exactly three rungs. A rung you added yourself holds no
reserved role, so there is no field for it and no way to guess one. That is the
whole difference:
On the default three rungs the two are equivalent, and a path is simply the
longer way to say the same thing. A path starts to matter only when your ladder
has a rung the ids cannot name.
To address a level beyond those three, pass a scope path from the SDK, naming
every rung by its key. On a read it is the
scope_path argument:
scope argument on memories.create:
- Every rung must be named. A path that skips a level is refused, and the error names the level you left out. Synap never infers a missing id, because a wrong guess at or above the organization level would cross a tenant boundary.
- The feature must be enabled for your instance. If it is not, sending a
scopemap is an error rather than being ignored. Ignoring it would widen the request to the whole organization when you asked to narrow it to one team.
Transcript ingest does not take a scope path in any released SDK yet. If your
ladder has a rung between your customer and your user and you push
transcripts, talk to us before you add it.
What you can reach today
Being precise about this matters more than making the feature sound finished.
The honest summary: Synap’s scoping is correct and safe on the shape accounts
already use. Driving a different shape needs an SDK new enough to send a scope
path, and on writes that means
memories.create.
Which of these you can reach is decided by what your own integration sends, and
that is something you can change. You do not have to go and find out what it
sends: the Scope Ladder page in your dashboard lists the SDK versions we have
seen in your recent requests, tells you the oldest one, and says so plainly when
it has not seen any requests from you at all.