> For the complete documentation index, see [llms.txt](https://docs.emseapea.ai/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.emseapea.ai/builder-guide/kinds-of-access.md).

# The two kinds of access

Every system your app touches — Salesforce, Microsoft Dataverse, Microsoft 365, whatever your organization has on its list — needs an answer to one question: **whose permissions does the app use?** There are only two possible answers, and you don't need to know anything about OAuth, tokens, or how sign-in works under the hood to choose correctly between them.

> **This page is about data that already exists somewhere else.** If what you actually need is somewhere for your app to keep its **own** files or records — an uploaded receipt, a generated report, a bookings table — that is a different thing with a different answer, and the question on this page does not apply to it: nobody signs in to your app's own storage. See [Storing files and data in your app](/builder-guide/storing-files-and-data.md). Plenty of apps need both.

## The two kinds, in plain words

### Only what the person using it can already see

The app acts as whoever is signed in, right now, using it. Salesforce, Microsoft and Google apply **that person's own permissions** — the app can never show someone more than they could already see by signing in directly themselves.

> **Worked example.** Priya builds a rota app that reads Microsoft Dataverse. Priya signs in to use it, and it shows exactly the shifts and stores her own Dataverse account can see. Her colleague Tom opens the same app the next day — he sees only what **Tom's own** Dataverse account can see, which might be a different set of stores, or none at all if Tom has no Dataverse account. The app didn't change. Who's looking at it did.

Because this kind can never show anyone more than they could already get by signing in to the system themselves, it needs no extra sign-off — it's approved automatically.

### Wider access, set up by IT

The app acts as an account **IT prepared and named in advance** — a "prepared account" — so it may show data the person using it could not normally see on their own. This always needs IT's approval.

> **Worked example.** Katie wants a supplier-contract tracker that shows every supplier contract in Salesforce, not just the ones she personally has visibility into. An admin sets up a prepared account called "All supplier contracts," limited to read-only access on the Contracts object, and Katie's app requests Salesforce as that account. Because it can show more than Katie herself could see by signing in to Salesforce directly, an approver has to say yes before the app goes live.

## Alice builds a dashboard. Bob opens it. Whose data does Bob see?

This is the question everyone asks, so here is the direct answer — both of them:

* **If the app was built with "only what the person using it can already see"** — Bob sees **Bob's own data**, determined by Bob's own permissions in whatever system the dashboard reads. Not Alice's. It doesn't matter that Alice built it, tested it, or could see more (or less) than Bob when she did — once it's live, each person who opens it sees through their own permissions.
* **If the app was built with "wider access, set up by IT"** — Bob sees whatever the prepared account can see. The **same data**, regardless of whether it's Alice, Bob, or anyone else with access to the app looking at it — because the app isn't using either of their permissions; it's using the prepared account's.

What decides which of these happens is not who built the app, and not who happens to be looking at it at any given moment — it's **which kind of access was requested and approved, per system**, when the app was built. That choice is fixed once approved; nobody's individual permissions creep in afterwards by accident.

## Mixing the two in one app

An app doesn't pick one kind overall — it picks one **per system**, and using different kinds for different systems in the same app is completely normal, not an edge case.

> **Worked example.** A "my week" app reads your Google calendar as **whoever is signed in** (it should only ever show your own meetings — using a prepared account here would leak everyone's calendars to everyone), and reads Salesforce as a **prepared account** to show org-wide pipeline totals alongside it (no individual salesperson's own Salesforce access adds up to the whole org's number).

Different systems answer different kinds of questions. "What's on my calendar today" is naturally a question about one person. "What's the total pipeline value across the whole sales team" isn't — no single person's own access would ever answer it completely. Picking the kind that matches the question, per system, is the normal shape of a governed app.

## When someone doesn't have an account

This only matters for the first kind — a prepared account is IT's to keep working, so nobody using an app that relies on one is ever missing an account for it.

If your app uses "only what the person using it can already see" for a system, and the person currently signed in has no account there at all, the honest answer is that the app cannot show them anything from that system — and it should say so plainly rather than fail with an error. The person sees:

> You need a \<System> account to see this. Ask your IT team.

Not a 403, not "unauthorized," not a stack trace — they didn't do anything wrong; they just don't have an account there yet.

## What the builder has to write

Signing in is handled for you — the template your app is generated from does that part. Reaching a system afterwards is always the same shape: your app sends **its own credential handle** to your organization's gateway, never a vendor token to the vendor directly (every vendor's own tutorial will tell you the opposite; it's describing ungoverned access). The exact request shape for each system you've been approved for — base URL, gotchas, copy-pasteable examples — is generated into your repo as `CONNECTORS.md` the moment your app is approved, so you don't have to guess it.

**One honest exception, today.** For Microsoft 365, "only what the person using it can already see" is backed by a real, per-person check: the gateway needs to know *who* is signed in to get a token scoped to exactly that person. That means every request to Microsoft 365 also needs an `X-Emseapea-Person` header, alongside your credential handle. **The template does not send this for you yet** — you add it yourself, forwarding the same sign-in token your app already holds for the current person:

```http
GET {GATEWAY_URL}/u/msgraph/v1.0/me/messages
Authorization: Bearer {CREDENTIAL_HANDLE}
X-Emseapea-Person: {SIGNED_IN_PERSON_TOKEN}
```

Leave it out and the call still works, but against a stand-in credential rather than the signed-in person — don't leave it out on purpose. `CONNECTORS.md` restates this for you if Microsoft 365 is one of your app's systems.

Microsoft is also the only system where this promise is a real, independently checked exchange today. For Salesforce and Dataverse, choosing "only what the person using it can already see" still decides whether your request needs approval and is the record of what you asked for — but the vendor call itself isn't yet separately checked against who's using the app at that moment the way Microsoft's is. If you need that stronger guarantee for a system other than Microsoft 365, ask your admin whether it's available yet before you build on the assumption that it is.

## Curating the "wider access" accounts

If you're the one setting up prepared accounts rather than requesting one, see [Curating prepared accounts](/admin-guide/prepared-accounts.md) for what makes a good "limited to" description — the words an approver will read instead of just "wants Salesforce."

The reasoning behind why access works this way at all — and what is and isn't actually enforced — is recorded in **D7, "Fine-grained access scope,"** in the [product's architecture decisions](https://github.com/scmjea/emseapea/blob/main/docs/architecture.md) (engineering repo access required), for anyone who wants it.
