Skip to navigation

API Scoping & Access Control

Understanding OAuth apps, API users, and data access permissions

Data Access and API Scoping

All API integrations in Ottimate operate through OAuth applications tied to a specific API User. Understanding how data access is scoped is critical for integrations, especially in multi-company accounts.

OAuth Apps and API Users

When you integrate with Ottimate:

  1. OAuth App - Your integration application authenticates using Client ID and Client Secret
  2. API User - Every OAuth app operates on behalf of an API User provisioned by Ottimate
  3. Access Scope - The API User determines what data your integration can access

Access Scoping Levels

API Users can be scoped at three levels:

Scope LevelAccessUse Case
Account-levelAll companies and locations within the accountMaster integrations managing multiple subsidiaries
Company-levelSingle company and all its locationsERP integration for one legal entity
Location-levelSpecific location(s) onlyRestricted integration for a single site

Examples:

Account-Scoped API User:

// Can access data across all companies
GET /v1/invoices?ottimate_company_id=123 // ✅ Allowed
GET /v1/invoices?ottimate_company_id=789 // ✅ Allowed
GET /v1/vendors?ottimate_company_id=123 // ✅ Allowed

Company-Scoped API User (Company 123 only):

// Can only access Company 123 data
GET /v1/invoices?ottimate_company_id=123 // ✅ Allowed
GET /v1/invoices?ottimate_company_id=789 // ❌ Forbidden
GET /v1/vendors?ottimate_company_id=123 // ✅ Allowed

Location-Scoped API User (Location 456 only):

// Can only access Location 456 data
GET /v1/invoices?ottimate_location_ids=456 // ✅ Allowed
GET /v1/invoices?ottimate_location_ids=457 // ❌ Forbidden

Resource Scoping in API Calls

Every API request that creates or retrieves data must specify the appropriate company and/or location scope.

Company Scoping

Most API endpoints require ottimate_company_id to specify which company’s data you’re working with.

Examples:

# List all vendors for a specific company
GET /v1/vendors?ottimate_company_id=123
# Create a dimension for a specific company
POST /v1/dimensions
{
"ottimate_company_id": 123,
"erp_type": "Account",
"ottimate_type": "account",
"name": "Office Supplies",
"code": "5010",
"reference_id": "acct-office-supplies-001",
"erp_dimension_id": "5010"
}
# List invoices for a specific company
GET /v1/invoices?ottimate_company_id=123

Location Scoping

Invoices must be assigned to a specific location. Other resources (vendors, dimensions) are company-level and shared across all locations.

Examples:

# Create an invoice for a specific location
POST /v1/invoices
{
"ottimate_company_id": 123,
"ottimate_location_id": 456,
"ottimate_vendor_id": 789,
"invoice_number": "INV-001",
"total_amount": 1000.00
}
# Filter invoices by location
GET /v1/invoices?ottimate_company_id=123&ottimate_location_ids=456
# Filter invoices by several locations at once (comma-separated)
GET /v1/invoices?ottimate_company_id=123&ottimate_location_ids=456,457

Common Error: Attempting to create an invoice with an ottimate_location_id that doesn’t belong to the specified ottimate_company_id will result in a validation error.

Singular in bodies, plural in list filters. Write operations take a single ottimate_location_id (an integer) in the request body, because an invoice belongs to exactly one location. List endpoints such as GET /v1/invoices take ottimate_location_ids (plural) — a comma-separated string, so you can filter across several locations in one call. GET /v1/invoices/uploads is the exception: it filters by a single ottimate_location_id.

Finding the Right IDs

To determine which company and location IDs to use:

Step 1: List all companies

GET /v1/accounts/{account_id}/companies

Step 2: List all locations

GET /v1/accounts/{account_id}/locations

Step 3: Match to your business logic

When creating an invoice, determine which company and location it belongs to based on your business rules (e.g., which ERP it came from, which site received the goods).