SFDC Permissions Requirements

Last updated: September 8, 2026

Overview

Tabs connects to Salesforce through a managed OAuth app and acts entirely as the Salesforce user who authorizes the connection. Everything Tabs can see or change is bounded by that user's object and field permissions.

At a high level Tabs needs:

  • Read on the merchant's customer, opportunity, and file objects — and on every field of those objects, because Tabs reads all fields rather than a fixed subset.

  • Create, Read, Edit on three Tabs-owned custom objects that hold pushed invoices, their line items, and the link back to the source record.

  • Edit on Account, limited to one Tabs-created field. Tabs writes the Tabs customer identifier back to a custom field it adds to Account, called Tabs Customer ID, so a Salesforce user can cross-reference the record in Tabs. No other Account field is ever written.

  • Create on files (to attach invoice PDFs) and Delete on files, used only to remove a superseded Tabs-generated invoice PDF.

  • One-time administrative access to create those custom objects and fields, and grant field access to them. This is only needed during setup, and can be handled by the merchant's admin instead of granted to Tabs.

Apart from the Tabs Customer ID field on Account, Tabs does not write to standard Salesforce objects — no native Account field is modified, and Contact and Opportunity are entirely read-only.


1. Connection and authentication

Item

Value

Auth method

OAuth 2.0 authorization code with refresh token

App

"Tabs SFDC Client App" — a Salesforce External Client App published by Tabs

OAuth scopes requested

Api, Web, Full, RefreshToken

Salesforce API

REST API v59.0, plus the Metadata API during one-time setup

Environments

Production and Sandbox are separate connection types; a sandbox connection authorizes against the Salesforce test login domain

Two consequences worth stating explicitly to a Salesforce admin:

  1. Tabs has no independent identity in the org. Every read and write is performed as the authorizing user, so that user's profile and permission sets are the real access-control boundary — not the OAuth scopes.

  2. The Full scope does not grant more than the user has. It only means Tabs is not additionally narrowed at the OAuth layer; object- and field-level security still applies. Merchants who want a tighter footprint can authorize with a dedicated integration user carrying only the permissions in this document — but only once provisioning is done, because provisioning itself needs administrative permissions (1.2, section 6).

The authorizing user must have API Enabled on their profile.

1.1 Why Tabs requests the Full scope

The Tabs Salesforce integration is configured per merchant, not hardcoded. There is no fixed list of objects and fields Tabs reads — the merchant decides which Salesforce object represents a customer, which represents a contract, which fields feed each Tabs field, and which related objects Tabs should walk to assemble a record. Different merchants point Tabs at completely different objects, including custom ones.

That configuration step is only possible if Tabs can first perform a full schema discovery: enumerate the org's entire object catalog, then read the field definitions and relationships of the objects the merchant is choosing between. Tabs cannot know in advance which objects those will be, so it cannot request a scope narrowed to a specific set of objects. The Full scope is what makes the whole catalog visible to the mapping UI.

The other scopes cover the remaining mechanics:

Scope

Why it is requested

Full

Schema discovery across the entire object catalog, so the merchant can map any object or field; also covers the Metadata API calls used for one-time provisioning

Api

The ongoing REST API reads and writes described in section 2

Web

Completing the browser-based OAuth authorization flow

RefreshToken

Keeping the connection alive without re-authorizing; syncs run on a schedule, unattended

Two things the Full scope does not do, both worth stating to a security reviewer:

  • It does not expand what Tabs can reach. OAuth scope is a ceiling, not a grant. Tabs still acts as the authorizing user, and every object, field, record, and sharing rule that applies to that user applies to Tabs. Discovery itself returns only the objects and fields that user can already see.

  • It does not mean Tabs reads the whole org. Discovery reads metadata — object and field definitions. Record data is only ever pulled from the objects the merchant explicitly maps.

1.2 Narrowing the scopes

A merchant who does not want to grant a scope can restrict it on their side. Salesforce lets an admin limit the OAuth scopes an app is permitted, and control which users or profiles may authorize it, from the connected app's policies in Setup. Tabs does not need to publish a different app for this.

The functional trade-offs:

If the merchant withholds

Effect

Full

Schema discovery cannot enumerate the org. The merchant cannot self-serve field mapping in the Tabs UI; the object and field set has to be configured manually with Tabs, and one-time provisioning must be done by an admin (section 6)

RefreshToken

The connection expires and syncs stop until someone re-authorizes by hand

Api

The integration cannot function at all

Web

The connection cannot be authorized at all

Constraining the authorizing user is not a substitute, at least not at setup.

Because scope is only a ceiling, it is tempting to keep the scopes and authorize with a narrow integration user instead. That does not work for the initial connection: the setup steps themselves need administrative permissions, so the authorizing user has to be an admin or admin-equivalent while they run.

Three things drive that:

Setup step

Needs

Creating the Tabs custom objects, their fields, and the Account field

Customize Application, plus Modify Metadata Through Metadata API Functions in orgs that gate the Metadata API separately

Locating the Standard User and System Administrator permission sets

View Setup and Configuration

Granting read/edit on the newly created fields, including on the System Administrator profile's own permission set

Manage Profiles and Permission Sets

So the way to end up with a genuinely narrow user is to take the administrative work out of Tabs' hands, not to narrow the user Tabs authorizes as:

  1. A Salesforce admin creates the custom objects and the Account field manually, to the specification in section 4, and sets field-level security themselves.

  2. Tabs detects that they already exist and skips creation entirely — both the object and the field paths check first and no-op if present.

  3. The connection is then authorized as a dedicated integration user holding only the permissions in section 5, with no administrative permissions at all.

Ongoing syncs never need the administrative permissions; only provisioning does. The one wrinkle is that the Account field is re-checked on every customer sync rather than only at setup, so if it is later deleted, a narrow integration user cannot recreate it (section 6).


2. Workflow overview

2.1 Schema discovery (setup, and on demand afterwards)

When a merchant connects Salesforce, Tabs reads the org's object list and then reads the field definitions of the objects the merchant selects. This powers the field-mapping UI, where the merchant tells Tabs which Salesforce object represents a customer, which represents a contract, and which Salesforce field feeds each Tabs field.

Field definitions are read in batches, and the resulting schema — objects, fields, and the relationships between them — is stored on the Tabs side so that routine syncs never need to re-read org metadata.

2.2 One-time provisioning of Tabs objects

Before invoices can be pushed, Tabs (or the merchant's admin) creates three custom objects in the org: the Tabs invoice object, the Tabs invoice line item object, and a junction object that links a Tabs invoice to the Salesforce record it originated from. Tabs then grants read and edit access on the new fields to the Standard User and System Administrator profiles so the records are visible to ordinary users.

This step runs once per org. If an admin creates the objects manually to the same specification, Tabs detects that they already exist and skips creation — which means the administrative permissions in section 6 do not have to be granted to Tabs at all.

2.3 Customer pull (recurring, incremental)

Tabs pulls customers from the object the merchant designated as the customer record — Account by default. From each customer record Tabs derives:

  • the customer itself (name, currency, tax-exempt status),

  • contacts (default: the related Contact records),

  • billing and shipping addresses,

  • any additional custom fields the merchant mapped.

If the customer object is Account, Tabs also reads the account's parent so the customer hierarchy can be reproduced in Tabs.

Merchants can map fields that live on related objects, not just the customer record itself. Tabs walks those relationships — potentially several hops — and assembles one combined customer record per pull. The set of objects involved is therefore merchant-specific, derived from the merchant's mapping configuration rather than fixed by Tabs.

Runs are incremental: Tabs tracks the last-modified timestamp it has already processed and only pulls records changed since. Merchants can also apply filters (for example, only accounts of a certain type, or only opportunities past a certain stage) so Tabs pulls a narrower slice of the org.

2.4 Customer ID write-back to Account (recurring)

After a customer sync, Tabs writes the Tabs customer identifier back onto the matching Account record so a Salesforce user can cross-reference the account in Tabs. This is the one place Tabs writes to a standard object.

Mechanically:

  1. Tabs checks whether a custom field named Tabs Customer ID (Tabs_Customer_Id__c, Text 36) exists on Account. If not, it creates it, and grants read/edit on it to the Standard User and System Administrator permission sets so the value is visible to ordinary users.

  2. Tabs queries the Accounts it has synced to see which already have the field populated.

  3. Tabs updates only the Accounts where the field is empty, in batches.

Two properties that limit the blast radius:

  • Tabs never overwrites a populated value. An Account that already carries a Tabs Customer ID is skipped entirely, so re-runs are inert.

  • No other Account field is touched. The update payload contains the record identifier and the Tabs Customer ID field only.

This runs on the customer sync rather than once at setup, so the field creation in step 1 is attempted — and skipped as already-existing — on an ongoing basis.

A merchant who does not want this behaviour has two options. It can be switched off per merchant on the Tabs side, though the same switch also disables pushing CRM customers onward to the merchant's accounting system, so check that first.

Alternatively, simply do not grant Account edit: the write-back fails, is logged, and the customer pull continues unaffected.

Note that this write targets Account specifically, even for merchants who mapped a different object as their customer record. It does not follow the customer-object mapping.

2.5 Contract and document pull (recurring, incremental)

Contracts in Salesforce are files. Tabs pulls file records — ContentDocument by default — resolves which business record each file is attached to (typically an Opportunity or Account), and links the file to the corresponding Tabs customer.

Only PDFs are carried through to contract processing; the file extension on the Salesforce file record is what Tabs uses to determine that.

A second pass exists because of a Salesforce behaviour worth flagging: attaching or renaming a file on an already-closed Opportunity does not update that Opportunity's last-modified timestamp. An incremental pull driven by the Opportunity would therefore never revisit it. Tabs handles this by separately scanning files by their own last-modified timestamp, resolving what they are attached to, and re-reading those records.

Tabs stores a download link for each file rather than the file bytes, and downloads the file content when contract processing needs it.

2.6 Invoice push (recurring)

When an invoice is created or changes status in Tabs, Tabs writes it to Salesforce:

  1. Look up whether a Tabs invoice record already exists in the org for that Tabs invoice. If it does, update it in place; if not, create it.

  2. Create the junction record linking the Tabs invoice to the source Salesforce record (by default an Opportunity, or whatever object the merchant configured as the push parent). Junction records are created once and never rewritten.

  3. Create or update one line item record per invoice line.

  4. Optionally attach the invoice PDF to the source record as a Salesforce file. Before attaching, Tabs removes any previously attached file with the same invoice title so the record does not accumulate stale copies of the same invoice.

Two design points that affect permissions:

  • Tabs matches existing records by querying, not by upsert-on-external-id. Older orgs were provisioned with the Tabs identifier as a plain text field rather than a flagged External ID, and an upsert would fail there. Querying works in both cases — but it means Tabs needs read access on its own objects, not just create and edit.

  • Invoice creation is deliberately not retried, so a lost response cannot produce a duplicate invoice in the org. Updates are retried.

2.7 What Tabs does not do in the org

  • No writes to standard objects other than the single Tabs-created Tabs Customer ID field on Account (2.4). No native Account field is modified, and Contact and Opportunity are entirely read-only.

  • No deletion of any standard record. The only deletes Tabs issues are of Tabs-generated invoice PDFs it is about to replace, matched by title.

  • No Apex, triggers, flows, scheduled jobs, or layout changes.

  • No reading of unrelated objects. Tabs reads the objects the merchant mapped, plus the file objects described above.


3. Objects and fields Tabs reads

3.1 An important caveat on field-level security

For the objects it pulls, Tabs requests all fields rather than a named list. This is intentional — it lets merchants map any field without Tabs needing to re-read org metadata on every sync — but it has a direct permissions consequence:

Tabs only receives fields the authorizing user can read. A field hidden by field-level security is silently absent, and any Tabs mapping pointing at it resolves to empty. Mappings do not error; the data is simply blank.

So field-level read should be granted on all fields of the objects listed below, or at minimum on every field the merchant maps plus the record identifier and last-modified timestamp. If a merchant reports missing or blank customer data after a successful sync, field-level security is the first thing to check.

3.2 Baseline read objects

These are the defaults. A merchant's mapping configuration can add to this list (see 3.3).

Object

Why Tabs reads it

Fields

Account

Default customer record

All fields. The parent account field is used to reproduce the customer hierarchy; the last-modified timestamp drives incremental sync. The Tabs Customer ID field is also read, to skip Accounts already stamped (see 2.4)

Contact

Default contact source for customers

All fields. Name, email, and phone are the mapped defaults

Opportunity

Default source record for contracts, and default parent for pushed invoices

All fields when used as a contract source. When used only as the push parent, Tabs reads its field definitions and matches records by identifier

ContentDocument

The contract/attachment file record

All fields. Title, file extension, latest published version, and last-modified timestamp are load-bearing

ContentDocumentLink

Resolves which business record a file is attached to

Record identifier, the file identifier, and the linked record identifier

ContentVersion

Downloading the actual file content for contract processing

The version data (file body)

ContentDocumentLink also carries Salesforce's own visibility rules: Tabs can only see links the authorizing user can see, so a file shared narrowly may not be visible to an integration user even with full object read.

3.3 Merchant-configured objects

Because mapping is merchant-driven, the read surface is not fixed. Any object a merchant selects as their customer record, contract record, or as an intermediate hop in a mapped relationship becomes part of the read requirement — including custom objects.

Common additions seen in practice: Contract, Quote, Order, User (as an owner reference), and merchant-specific custom objects standing in for Account or Opportunity.

Rule of thumb for building the permission set: grant read on every object named in the merchant's Tabs field-mapping configuration, with field-level read on all of their fields.


4. Objects and fields Tabs writes

Writes go to Tabs-owned custom objects, Salesforce files, and one Tabs-created custom field on Account. Nothing else in the org is modified.

4.1 Account — one Tabs-created field

Field

API name

Type

Written on

Tabs Customer ID

Tabs_Customer_Id__c

Text (36)

Set once, on the first customer sync after the Account is matched to a Tabs customer. Never overwritten once populated

The field is created by Tabs if absent, is not flagged unique or as an External ID, and holds the Tabs customer identifier. It requires Edit on Account plus field-level edit on this one field — no other Account field is written.

4.2 Tabs invoice object — Tabs_Invoice__c

One record per Tabs invoice. Created with sharing model Read/Write, with activities and field history enabled.

Field

API name

Type

Written on

Invoice Number (record name)

Name

Text

Create and update

Tabs Id

Tabs_Id__c

Text (36)

Create and update — the match key

Status

Status__c

Text (20)

Create and update

Total

Total__c

Currency (18,2)

Create and update

Balance Remaining

Balance_Remaining__c

Currency (18,2)

Create and update

Issue Date

Issue_Date__c

Date

Create and update

Sent Date

Sent_Date__c

Date

Create and update

Due Date

Due_Date__c

Date

Create and update

Tax

Tax__c

Currency (18,2)

Create and update

The record name is the Tabs invoice number prefixed with INV-. Status is the Tabs invoice status. Tax is the total sales tax computed from the invoice's line items rather than a single field copied from Tabs.

4.3 Tabs invoice line item object — Tabs_Invoice_Line_Item__c

One record per invoice line. Same sharing and history settings as the invoice object.

Field

API name

Type

Notes

Invoice Line Item Number (record name)

Name

Text

Standard record name

Tabs Parent Invoice Id

Tabs_Parent_Invoice_Id__c

Lookup to Tabs_Invoice__c

Required; delete-constrained, so an invoice with lines cannot be deleted

Tabs Id

Tabs_Id__c

Text (36)

Match key

Name

Name__c

Text (255)

Item

Item_Type__c

Text (255)

Currently carries the same value as Name__c

Description

Description__c

Text (255)

Quantity

Quantity__c

Number (18,2)

Total

Total__c

Currency (18,2)

4.4 Junction object — Tabs_Invoice_Opportunity__c

Links a Tabs invoice to the Salesforce record it came from. The object name is derived from the push parent object — with the default Opportunity parent it is Tabs_Invoice_Opportunity__c, and the parent lookup field is Opportunity__c.

Field

API name (default parent)

Type

Parent lookup

Opportunity__c

Lookup to the parent object

Invoice lookup

Tabs_Invoice__c

Lookup to Tabs_Invoice__c

Tabs External Id

Tabs_External_Id__c

Text (80), External ID, unique

Junction records are insert-only — Tabs creates one when it first links an invoice to a parent and never updates it. Note that Tabs_External_Id__c is provisioned for historical reasons but the current push path does not populate or key on it.

4.5 Salesforce files

Action

Object

Scope

Create

ContentVersion

Uploading the invoice PDF, attached to the source record. Tabs supplies the title, filename, file body, and the record to attach it to

Read

ContentDocumentLink

Finding files already attached to the source record, to detect a prior copy of this invoice

Delete

ContentDocument

Narrowly scoped — Tabs deletes a file only when its title matches the invoice PDF title it is about to write, i.e. only its own superseded output


5. Permissions summary

Grant these on the profile or permission set assigned to the authorizing user.

5.1 Object permissions

Object

Read

Create

Edit

Delete

View All

Account

(one field only)

recommended

Contact

recommended

Opportunity

recommended

Any other merchant-mapped object

recommended

ContentDocument

ContentDocumentLink

implicit

ContentVersion

Tabs_Invoice__c

Tabs_Invoice_Line_Item__c

Junction (Tabs_Invoice_Opportunity__c)

On Account Edit: it is needed only to write the Tabs-created Tabs Customer ID field (2.4, 4.1). Combined with field-level security limited to that one field, Account is effectively read-only for every native field. Withholding Account edit disables the write-back and nothing else.

On "View All": it is recommended, not required. Without it, Tabs sees only records the authorizing user can see through sharing rules, and a customer or opportunity outside that visibility will silently never sync. Merchants who prefer not to grant View All should authorize with a user whose sharing visibility covers everything they expect Tabs to pull, and should expect selective gaps otherwise.

On Delete: ContentDocument delete is the only delete permission Tabs needs, and it is used exclusively to replace a Tabs-generated invoice PDF. Merchants who do not want Tabs attaching PDFs at all can withhold both ContentVersion create and ContentDocument delete; invoice, line item, and junction records still push normally.

5.2 Field-level security

Scope

Requirement

All fields on the read objects in section 3

Read — see the caveat in 3.1

All fields on the three Tabs custom objects

Read and Edit

The Tabs Customer ID field on Account

Read and Edit — the only Account field needing edit

5.3 System permissions

  • API Enabled — required for all Tabs activity.


6. Administrative permissions — one-time provisioning only

These are only needed if Tabs creates the custom objects and the Account field. If a Salesforce admin creates them ahead of time to the specification in section 4, Tabs detects them and skips creation, and none of the following needs to be granted.

Permission

Used for

Customize Application

Creating the Tabs custom objects and their custom fields through the Metadata API, and the Tabs Customer ID field on Account

View Setup and Configuration

Locating the Standard User and System Administrator profile permission sets

Manage Profiles and Permission Sets

Granting read/edit on the newly created fields — including on the System Administrator profile's own permission set — so ordinary users can see Tabs invoices and the Tabs Customer ID

Modify Metadata Through Metadata API Functions

Metadata API access, if the org restricts it separately from Customize Application

Because these are broad org-level permissions, the recommended pattern is:

  1. An admin creates the objects and the Account field manually to the section 4 specification — or authorizes the connection with an elevated user for the one-time provisioning step.

  2. Provisioning completes and Tabs confirms the objects exist.

  3. If an elevated user was used, the connection is re-authorized as a dedicated integration user holding only the permissions in section 5. Swapping the user means re-running the OAuth authorization, not editing the existing connection.

The manual-creation route in step 1 is the only one that never grants Tabs an administrative session at all, which is usually what a security reviewer is actually asking for.

One exception to "one-time": the Tabs Customer ID field creation on Account is re-checked on every customer sync, not just at setup (2.4). Once the field exists, Tabs finds it and skips creation, so no administrative permission is needed on an ongoing basis — but if the field is deleted, an integration user without Customize Application cannot recreate it, and the write-back stays broken.