Provider documentation

Connect a service in Settings, then Provider Connections. Each guide below has the exact steps, permissions, and token format.

35 providers

Configure 1Password

Quick Summary

Auth OAuth 2.0 client credentials (client_id:client_secret)
Required scope List users
Required role Owner, administrator, or Security group member
Endpoint GET https://api.1password.{com\|ca\|eu}/v1beta1/accounts/{account_id}/users
User sync Yes
License sync No
Category identity
Billing seat rule assigned_until_removed

1. Prerequisites

  • A 1Password Business account (Enterprise Password Manager).
  • Permission to manage integrations (owner, administrator, or Security group).
  • Your account ID (shown in 1Password account settings / Integrations).
  • Account region: .com, .ca, or .eu.

Important: The Users API for Partners is a public preview. It is not supported when the account uses automated SCIM provisioning. In that case, manage users via your IdP instead.

2. Auth Model And Storage In A-Count

Item Value
Auth model OAuth 2.0 client credentials
What A-Count stores Encrypted Provider.api_token as client_id:client_secret; non-secret api_config.onepassword_account_id and api_config.onepassword_region
Encryption ENCRYPTION_KEY (Fernet) for the credential string
Token format in UI client_id:client_secret

Access tokens from 1Password last 15 minutes. A-Count mints a fresh token on each Test and Sync.

3. Steps In 1Password

  1. Sign in to your 1Password account on 1Password.com (or .ca / .eu).
  2. Open Integrations in the sidebar (then Directory if needed).
  3. Select OAuth Application.
  4. Configure:
    • Application name: e.g. A-Count
    • Redirect URL: any HTTPS URL (required by 1Password even for client credentials), e.g. your A-Count Settings URL
    • Scopes: enable List users (minimum). Optionally Get user.
  5. Generate credentials, copy the client ID and client secret (secret is shown once), and save them in 1Password.
  6. Note your account ID and region.

Docs: Users API get started, OAuth authorization.

4. Endpoints A-Count Calls

Capability Method Path Notes
Token POST /v1beta1/users/oauth2/token Basic auth with client credentials
Directory read GET /v1beta1/accounts/{account_id}/users Paginated via page_token
License / SKU — — Not available
Activity — — Not synced in v1

Rate limits: 100/min, 6,000/hour. A-Count retries 429 responses using Retry-After.

5. License Sync

  • User sync: Yes
  • License inventory sync: No (combined sync skips licenses)
  • Seats are inferred from synced users (ACTIVE ≈ billable under assigned_until_removed)

6. Installation In A-Count

  1. Open Settings → Provider Connections.
  2. Find 1Password and click Configure.
  3. Select API import mode.
  4. Enter Account ID and Region (com / ca / eu).
  5. Paste client_id:client_secret into the credentials field.
  6. Click Test, then Save.

7. What Gets Synced

Field Source
Email email
Name display_name
Status ACTIVE → active, SUSPENDED → suspended
Role Always user (API does not expose admin roles in list)
Provider user key User id
Last activity Not available

8. Known Limitations

  • Public preview API — fields and behavior may change.
  • Unsupported when automated SCIM provisioning is enabled on the account.
  • No org seat/SKU inventory endpoint.
  • No last_activity_at (Events API enrichment is out of scope for v1).
  • Suspend/reactivate write actions are not used.

9. Quick Troubleshooting

Error Cause Fix
Credentials rejected (401/403) on token Bad client id/secret Regenerate OAuth app credentials
Users API 401/403 Missing List users scope, or SCIM provisioning enabled Add scope or use IdP/SCIM instead of Users API
Account not found (404) Wrong account ID or region Match account ID to com / ca / eu
No users returned Empty account or wrong portal Verify credentials against the correct Business account

Configure a-count.io

Quick Summary

  • Required role: A-Count administrator (Settings → Users management)
  • Where to go: this A-Count instance (no vendor console)
  • Auth model: local database — Settings application users
  • Action: enable API mode (seeded automatically) and sync
  • Paste into the app: nothing — no API token
  • Security: no third-party credential is stored
  • API rights reference: api-rights-reference.md

1. Prerequisites

  • People who can sign in to A-Count already exist under Settings → Users management
  • No external API key, OAuth app, or vendor tenant is required

2. Auth Model And Storage In A-Count

Item Value
Auth model Local Settings users (User table)
What A-Count stores No Provider.api_token
Encryption N/A
Token format in UI None — Test confirms the Settings directory is readable

3. Steps In A-Count

3.1 Where To Go

  • Settings → Users management — create, invite, or deactivate people who can sign in
  • Settings → Provider Connections → a-count.io — already in API mode after seed

3.2 Create The Credential

Not applicable. Membership is the Settings user list.

3.3 Required Scopes / Permissions

Not applicable. Sync runs as the A-Count application against its own database.


4. Endpoints A-Count Calls

Capability Method Path / operation Permission required
Directory read SQL User table (Settings users) Local database
Identity read SQL email, name, role, is_active, last_login_at Local database
Per-user license — Seat assigned on sync: catalog package a-count.io (ref=acount) N/A
Org SKU / billing — No vendor inventory; first sighting materializes the catalog row N/A
Activity SQL User.last_login_at → last_activity_at Local database
Access groups SQL access_groups + members → team catalog external_team_id=access-group:{id} Local database

Secrets (hashed_password, MFA material) are not copied onto provider accounts.


5. License Sync

  • User sync: Yes
  • License inventory sync: Catalog-only — /licenses/sync/{id} returns licenses_upserted: 0. Combined sync does not skip. First user sighting materializes package ref=acount named a-count.io and assigns every Settings user to it.
  • Extra permissions for license sync: N/A

6. Installation In A-Count

  1. Open Settings.
  2. Confirm people exist under Users management.
  3. In Provider Connections, open a-count.io (import mode is API).
  4. Click Test (optional) then Save if you changed anything.
  5. Run Sync for a-count.io. Those Settings users appear as standard provider accounts on the Users audit, each assigned to the a-count.io license on the Licenses page. Access groups from Settings also appear under Users → Teams for a-count.io.

7. Known Limitations

  • This is not an external a-count.io tenant API; it inventories this instance’s application users
  • Inactive Settings users are included (status=inactive) so the audit can flag leftover access
  • Settings roles other than admin are stored as provider role user; the original role is in raw.settings_role
  • Access groups are this instance's rights groups, published as a-count.io teams. A directory-linked group lists only internal users who are still in that directory team. The level is stored on the team member (member_role)

8. Quick Troubleshooting

Test fails

  • Confirm the database is reachable and the user table exists
  • Restart the backend after deploying this provider so init_providers can seed the row

I Do Not See The API Option

  • Verify the provider slug is acount in PROVIDER_REGISTRY
  • CSV mode is hidden for this provider; users always come from Settings

Zero users after sync

  • Add at least one person in Settings → Users management, then sync again

9. Rotation & Security

  • No vendor token to rotate
  • Revoke access by deactivating or deleting the Settings user, then re-sync

Configure Adobe

Quick Summary

Auth OAuth 2.0 Server-to-Server (Adobe IMS)
Required scopes openid, AdobeID, user_management_sdk
Credentials Client ID (api_config.adobe_client_id) + encrypted Client Secret (api_token) + Organization ID
User sync Yes (UMAPI GET /users/{orgId}/{page})
License sync Yes (product profiles from GET /groups/{orgId}/{page})
Dual-mode Default CSV; switch to API in Settings
API rights reference api-rights-reference.md

Official docs:


1. Prerequisites

  • Adobe Business or Enterprise Admin Console (System Admin)
  • Verified domains under Admin Console → Settings → Identity (recommended)
  • Access to Adobe Developer Console to create an OAuth Server-to-Server project with User Management API

JWT / service-account integrations reached end-of-life (mid-2025). Use OAuth Server-to-Server only.


2. Auth Model And Storage In A-Count

Item Value
Auth model OAuth 2.0 client credentials via IMS (/ims/token/v2)
What A-Count stores Encrypted Provider.api_token = Client Secret; api_config.adobe_client_id; api_config.adobe_org_id; optional api_config.adobe_ims_host
Encryption ENCRYPTION_KEY (Fernet) for the client secret
Token format in UI Separate Client ID and Client Secret fields
Org ID Separate field (form A495E53@AdobeOrg)

Default IMS host: ims-na1.adobelogin.com. Override with adobe_ims_host only if Adobe documents a different regional IMS host for your tenant.


3. Steps In Adobe

3.1 Create OAuth Server-to-Server credentials

  1. Open Adobe Developer Console → Create project → Add API → User Management API.
  2. Choose OAuth Server-to-Server.
  3. Copy Client ID and Client Secret.
  4. Ensure scopes include openid, AdobeID, and user_management_sdk.
  5. In Adobe Admin Console, confirm the integration is authorized for your organization (System Admin).

3.2 Find the Organization ID

  • Admin Console URL path, or the Adobe Developer Console organization details.
  • Format: <hex>@AdobeOrg (example: A495E53@AdobeOrg).

4. Endpoints A-Count Calls

Capability Method Path / operation Permission required
Token POST https://{ims}/ims/token/v2 OAuth client credentials
Directory read GET /v2/usermanagement/users/{orgId}/{page}?directOnly=false user_management_sdk + X-Api-Key
Org SKU / profiles GET /v2/usermanagement/groups/{orgId}/{page} Same (product profiles with licenseQuota)
Activity — — Not available on UMAPI list users

Base URL: https://usermanagement.adobe.io.


5. License Sync

  • User sync: Yes — accounts with email, status, admin detection (_org_admin / _admin_*), and product-profile memberships
  • License inventory sync: Yes — one package per PRODUCT_PROFILE group (productName preferred, else groupName); seats from licenseQuota, assigned from memberCount
  • Per-user binding: User rows are expanded per product profile (similar to Google multi-SKU)
  • Cost: Manual in Licenses UI (UMAPI does not expose purchase price)

6. Installation In A-Count

  1. Open Settings → Provider Connections → Adobe → Configure.
  2. Select Import mode = API.
  3. Paste Organization ID, Client ID, and Client Secret as separate fields.
  4. Click Test, then Save.
  5. Run user sync (and license sync — included in combined provider sync).

Keep CSV mode if you do not have Developer Console access; A-Count does not auto-fallback during sync.


7. Known Limitations

  • No reliable last-activity field on the users list API (last_activity_at stays empty)
  • Groups endpoint is heavily throttled (≈5 req/min); sync paces calls and retries 429
  • licenseQuota may be non-numeric for some profiles (seats left unset)
  • SCIM is not used in v1 (UMAPI only)

8. Quick Troubleshooting

Symptom Likely cause
OAuth 401/403 Wrong client id/secret, or project missing User Management API
UMAPI 401/403 Org id wrong, or integration not added to the Admin Console org
429 Too Many Requests Transient throttle — A-Count retries with backoff
Users OK, no licenses No PRODUCT_PROFILE groups returned
Empty product profiles on users Groups fetch failed; check throttling / permissions

9. Rotation & Security

  • Rotate the Client Secret in Adobe Developer Console regularly.
  • Revoke unused projects.
  • Credentials are encrypted at rest in A-Count.

Configure Atlassian

Quick Summary

Auth Organization API key (Authorization: Bearer)
Required Org Admin API key + Organization ID; Atlassian Guard + verified domain for emails
Credentials Encrypted org API key (api_token) + api_config.atlassian_org_id
User sync Yes (Admin API v2 directories → users; v1 managed-users fallback)
License sync Yes (assigned seats aggregated from productAccess)
Dual-mode Default CSV; switch to API in Settings
API rights reference api-rights-reference.md

Official docs:


1. Prerequisites (critical)

Without Atlassian Guard (formerly Access) and a verified domain, the Org Admin users API commonly returns missing or private emails. A-Count cannot correlate seats without email.

Prerequisite Why
Atlassian Guard (or trial) Authority over managed accounts so email is returned
Domain verification in Admin Hub Unlocks managed-user identity for the org
Organization API key Admin Hub → Settings → API keys (machine key; not a personal Jira token)
Organization ID From Admin Hub URL: admin.atlassian.com/o/<orgId>/…

If Guard is not purchased, keep CSV mode.


2. Auth Model And Storage In A-Count

Item Value
Auth model Bearer Organization API key
What A-Count stores Encrypted Provider.api_token = org API key; api_config.atlassian_org_id
Encryption ENCRYPTION_KEY (Fernet) for the API key
Org ID in UI Separate field (UUID)

3. Steps In Atlassian

  1. Confirm Guard is active and your company domain is verified in Admin Hub.
  2. Create an Organization API key (Settings → API keys) as an org admin.
  3. Copy the Organization ID from the Admin Hub URL.
  4. In A-Count Settings → Atlassian → API: paste Org ID + API key → Test → Save.
  5. Run user sync (license sync is included in combined provider sync).

Test fails with an actionable Guard/domain message when sample users systematically lack email.


4. Endpoints A-Count Calls

Capability Method Path Notes
Org check GET /admin/v1/orgs/{orgId} Credential / org ID probe
Directories GET /admin/v2/orgs/{orgId}/directories Cursor paginated
Users GET /admin/v2/orgs/{orgId}/directories/{directoryId}/users Includes productAccess + last active when present
Fallback users GET /admin/v1/orgs/{orgId}/users Used when no directories are returned

Base host: https://api.atlassian.com.


5. License Sync

  • Inventory packages are built by counting active users with each product key (jira-software, confluence, …).
  • Display names look like Atlassian Jira Software.
  • Seat totals here are assigned counts (not purchased spare seats from billing). Billing dollar amounts stay manual.

6. Troubleshooting

Symptom Likely cause Fix
Test: emails missing / Guard message No Guard or unverified domain Enable Guard + verify domain, or stay on CSV
401/403 Wrong key type or insufficient role Recreate Organization API key as org admin
404 Wrong org ID Confirm UUID from Admin Hub /o/<orgId>/
Rate limit 429 Admin API throttle Sync retries with backoff; re-run later if needed

Configure Autodesk (Insights / UMA-equivalent API)

Quick Summary

  • Modes:
    • import_mode = api (recommended) for manage.autodesk.com subscription users + seats via Business Success Plan Reporting (Insights)
    • import_mode = csv fallback when Premium Reporting is unavailable
  • Primary scopes: data:read data:write
  • Auth supported:
    • OAuth2 client credentials + Personal Access Token (PAT) (preferred for background sync)
    • Static 3-legged bearer token fallback (advanced)

Important: UMA vs BIM 360

Portal Example URL Used by a-count API sync?
Autodesk Account (UMA) https://manage.autodesk.com/uma/user-list/users Yes (via Insights exports)
BIM 360 / ACC Account Admin https://admin.b360.autodesk.com/... No (no longer used)

There is no public REST API that mirrors the UMA UI directly. Autodesk’s official programmatic source for subscription roster and seats is the Business Success Plan Reporting API (formerly Premium Reporting / Insights):

GET  /insights/v1/contexts
        POST /insights/v1/exports   # reports: USERS, SUBSCRIPTIONS
        GET  /insights/v1/exports/{exportId}
        

Requires Premium / Business Success Plan with ReportingAPIAccess on the team. Without it, connection test or sync fails with a clear error (it will not silently return the old BIM 1-user list).


1. Required Autodesk Setup

  1. Create an APS app in Autodesk Platform Services with access to Business Success Plan Reporting / Insights.
  2. Copy Client ID and Client Secret.
  3. As a team admin, open Autodesk Account → Profile → Security → Personal access tokens.
  4. Generate a PAT for Premium Reporting / Business Success Plan, linked to your APS Client ID.
  5. Confirm the team (e.g. SuperPlay) has Premium benefits in manage.autodesk.com.
  6. In a-count Settings → Autodesk:
    • Client ID / Client Secret
    • PAT
    • Optional Team name (SuperPlay) or Team ID (Insights contextId)
    • Scopes: data:read data:write

Auth calls mint a 2-legged token, then call Insights with Authorization: Bearer … and ADSK-PAT: ….


2. Provider Config (api_config)

  • autodesk_auth_mode: oauth_client_credentials (OAuth + PAT) or static_token
  • autodesk_client_id: APS Client ID
  • autodesk_pat: encrypted Personal Access Token (stored server-side)
  • autodesk_team_id: optional Insights context/team id
  • autodesk_team_name: optional team alias match (e.g. SuperPlay)
  • autodesk_scopes: default data:read data:write
  • autodesk_base_url: optional, default https://developer.api.autodesk.com

Client secret (or static bearer) is stored encrypted in provider api_token.

Legacy BIM fields (autodesk_account_id, autodesk_region) are ignored.


3. Data Synced (API mode)

  • Users: Insights USERS export (email, name, role/status when present, product assignments when present)
  • Licenses: Insights SUBSCRIPTIONS export (product/offering name, seat quantity / assigned — e.g. Maya 57 seats)
  • License model (v2): catalog ref = Autodesk product_id when present (else slugified product name). User sync is assign-only against inventory rows.

4. Troubleshooting

Symptom Likely cause
401 on contexts/exports Bad Client ID/Secret, expired PAT, or missing data:read/data:write
403 or empty teams Team lacks Premium / Reporting API access, or PAT user is not an admin
Team lacks ReportingAPIAccess Upgrade plan or select another team id/name
Fewer users than UMA UI PII hashing / export filters; confirm Personal Data reporting is enabled in team settings
Still seeing old BIM-only counts Restart API/worker after upgrading; re-save Autodesk settings with PAT

5. Notes

  • CSV import remains available for tenants without Premium.
  • Interactive 3-legged browser login is not implemented in Settings; use PAT for server sync.

Configure AWS (IAM Identity Center)

Quick Summary

  • Required role: AWS administrator (or delegated admin for IAM Roles Anywhere + Identity Center + Cost Explorer / Budgets)
  • Where to go: AWS IAM Roles Anywhere + IAM Identity Center (+ Cost Explorer / Budgets)
  • In AWS (EKS / EC2): Pod / instance role (IRSA) — no stored access key; uses the pod service account or instance profile via boto3’s default credential chain
  • Outside AWS (on-premises / Bizi): IAM Roles Anywhere — client certificate → temporary role credentials (no IAM users)
  • AssumeRole: optional caller access key; leave blank in Kubernetes to use the pod/instance IAM role as caller, then sts:AssumeRole into a reader role
  • Fallback: direct access keys (least preferred when DevOps forbids IAM users)
  • Paste into the app: Settings → Provider Connections → Configure → API mode
  • Security: private key is encrypted on save; certificate + ARNs are stored in api_config
  • API rights reference: api-rights-reference.md

Choose your deployment path

Where A-Count runs Auth mode in Settings What you store
EKS / EC2 in AWS — pod role already has IdC + CE/Budgets Pod / instance role (IRSA) Region only (no access key, no Role ARN)
EKS / EC2 in AWS — pod role is only a caller AssumeRole Region + Role ARN; leave access key blank
Outside AWS (on-prem, customer server) IAM Roles Anywhere Region, ARNs, client cert PEM, private key PEM
Legacy / dev only Access key (direct) AccessKeyId:SecretAccessKey

For Superplay (Kubernetes): if DevOps already attached Identity Center + Cost Explorer permissions to the pod IAM role, use Pod / instance role. If the pod role may only call sts:AssumeRole into ACountReadOnlyRole, use AssumeRole with Role ARN and a blank access key — DevOps must allow the pod role in the reader role’s trust policy.

For Bizi (outside AWS): use Roles Anywhere (§3 below).


1. Prerequisites

  • An AWS account (or organization management / member account) with IAM Identity Center enabled
  • Workforce users populated in the Identity Store (emails recommended)
  • Optional but recommended for inventory: Cost Explorer enabled, Budgets configured
  • For Pod / instance role: EKS IRSA or EC2 instance profile with the permissions in §3.2 attached to the runtime role (or to a reader role reachable via AssumeRole)
  • For Roles Anywhere: a private CA trust anchor (or AWS Private CA) and a signed client certificate for A-Count
  • For outside-AWS deployments only: Roles Anywhere is the IAM-role path without long-lived access keys

2. Auth Model And Storage In A-Count

Item Value
In-AWS auth aws_auth_mode=irsa (Pod / instance role) or assume_role with blank token
Outside-AWS auth aws_auth_mode=roles_anywhere
What A-Count stores (secret) Encrypted Provider.api_token = private key PEM (Roles Anywhere) or optional AccessKeyId:SecretAccessKey (AssumeRole / access key); empty for IRSA
Non-secret config aws_region, aws_auth_mode, aws_role_arn, aws_profile_arn, aws_trust_anchor_arn, aws_client_certificate_pem, optional aws_identity_store_id, optional aws_external_id
Encryption ENCRYPTION_KEY (private key / access keys only)
Legacy modes assume_role with caller keys, access_key direct

Important: Older Roles Anywhere handouts that scoped permissions to iam:ListUsers + License Manager are out of date. A-Count uses Identity Store users and Cost Explorer / Budgets inventory (below).

3. Steps In AWS (Roles Anywhere — for outside-AWS deployments)

You will create:

  1. Reader role — Identity Center list + Cost Explorer / Budgets
  2. IAM Roles Anywhere Trust Anchor + Profile that can assume that role for an A-Count client certificate
  3. Client certificate + private key issued against that trust anchor (handed to A-Count operators)

3.1 Prerequisites check

  1. Sign in to the AWS account where IAM Identity Center is enabled (usually the org management account, or the account that owns the IdC instance).
  2. Console → IAM Identity Center → confirm it is enabled and users exist.
  3. Note the Identity Center region (top-right region selector while on the IdC page), e.g. eu-west-1. Enter this as AWS region in A-Count.
  4. Optional: Billing → Cost Explorer (enable if prompted) and Budgets if you want spend inventory.

3.2 Create the read-only policy

  1. Console → IAM → Policies → Create policy → JSON.
  2. Paste:
{
          "Version": "2012-10-17",
          "Statement": [
            {
              "Sid": "IdentityCenterUsers",
              "Effect": "Allow",
              "Action": [
                "identitystore:ListUsers",
                "identitystore:DescribeUser",
                "sso:ListInstances",
                "sts:GetCallerIdentity"
              ],
              "Resource": "*"
            },
            {
              "Sid": "CostAndBudgets",
              "Effect": "Allow",
              "Action": [
                "ce:GetCostAndUsage",
                "budgets:ViewBudget",
                "budgets:DescribeBudgets",
                "iam:ListAccountAliases"
              ],
              "Resource": "*"
            }
          ]
        }
        
  1. Name it e.g. ACountAwsReadOnly → Create policy.

3.3 Create the reader role

  1. IAM → Roles → Create role.
  2. Trusted entity type: Custom trust policy. Use a temporary trust that Roles Anywhere will replace, or create the role first then update trust after the profile exists. Example once Roles Anywhere profile is known:
{
          "Version": "2012-10-17",
          "Statement": [
            {
              "Effect": "Allow",
              "Principal": {
                "Service": "rolesanywhere.amazonaws.com"
              },
              "Action": [
                "sts:AssumeRole",
                "sts:TagSession",
                "sts:SetSourceIdentity"
              ],
              "Condition": {
                "ArnEquals": {
                  "aws:SourceArn": "arn:aws:rolesanywhere:REGION:ACCOUNT_ID:trust-anchor/TRUST_ANCHOR_ID"
                }
              }
            }
          ]
        }
        
  1. Attach policy ACountAwsReadOnly.
  2. Role name e.g. ACountReadOnlyRole → Create role.
  3. Copy the Role ARN.

3.4 Configure IAM Roles Anywhere

  1. Console → IAM → Roles Anywhere (or IAM → Access management → Roles Anywhere).
  2. Create a Trust Anchor pointing at your CA (AWS Private CA or external CA whose roots Roles Anywhere should trust).
  3. Create a Profile that:
    • Allows the reader role from §3.3
    • Optionally constrains session policies further
  4. Copy Trust Anchor ARN and Profile ARN.
  5. Issue a client certificate (and private key) for A-Count, signed so Roles Anywhere will accept it under that trust anchor. Prefer short-lived certs and a rotation process.

Customer-facing PDF walkthroughs for Roles Anywhere certificate issuance may still be useful for CA steps; ignore any outdated permission lists in those PDFs and use §3.2 instead.

3.5 What to paste into A-Count

A-Count field Value
Import mode API
Auth mode IAM Roles Anywhere (recommended)
AWS region (Identity Center) Region from §3.1 (e.g. eu-west-1)
Role ARN Reader role ARN
Profile ARN Roles Anywhere profile ARN
Trust Anchor ARN Roles Anywhere trust anchor ARN
Client certificate (PEM) Full -----BEGIN CERTIFICATE----- … PEM
Private key (PEM) Matching private key PEM (encrypted on save)
Identity Store ID Leave blank (auto-discover) unless ListInstances fails

Then Test → Save → run sync.

3.6 If Test fails

Error hint Fix
CreateSession / Roles Anywhere denied Trust anchor, profile ↔ role mapping, cert chain, or wrong ARNs
sts:TagSession AccessDenied on AssumeRole IRSA role-chaining: add sts:TagSession (with sts:AssumeRole) on the reader role trust policy for the pod role principal, and on the pod role identity policy for the reader Role ARN. A-Count does not send session tags; EKS IRSA sessions often still require TagSession. See §3.7.
identitystore:ListUsers denied Attach ACountAwsReadOnly to the reader role
No IAM Identity Center instance Wrong account/region, or IdC not enabled — set region to the IdC region
Cost Explorer / Budgets errors Enable CE in Billing; budgets permission optional for users-only

3.7 Fallbacks (IAM users and AssumeRole)

  • Pod / instance role (IRSA): attach ACountAwsReadOnly (§3.2) directly to the EKS service-account role or EC2 instance profile. In A-Count: auth mode Pod / instance role, no access key, no Role ARN.
  • AssumeRole from Kubernetes (cross-account):
    1. Pod/instance role identity policy: allow sts:AssumeRole and sts:TagSession on the reader Role ARN.
    2. Reader role trust policy: Principal = pod role ARN; Actions = sts:AssumeRole and sts:TagSession (IRSA sessions carry OIDC/session tags during role chaining).
    3. In A-Count: auth mode AssumeRole, paste Role ARN, leave access key blank. Example (Superplay-style): caller arn:aws:iam::975050070381:role/prod-a-count-io → reader arn:aws:iam::127494504170:role/ACountReadOnlyRole.
  • AssumeRole with IAM user caller: caller IAM user with only sts:AssumeRole into the reader role + External ID; paste AccessKeyId:SecretAccessKey (only if IAM users are allowed).
  • Access key (direct): attach ACountAwsReadOnly to one IAM user (least preferred).

Many DevOps teams forbid IAM users entirely — use Pod / instance role or Roles Anywhere (§3.2–3.5) instead.

  • Not applicable (no OAuth app install). Rotate client certificates / private keys periodically.

3.9 After IAM fix — retest checklist

  1. Settings → AWS → API → AssumeRole, Role ARN set, access key blank → Test.
  2. Expect success (Identity Center sample users) or a permission error on Identity Store / CE — not sts:TagSession.
  3. Save, then run Sync. Settings list should not show Missing token for IRSA / keyless AssumeRole.

4. Endpoints A-Count Calls

Capability Method Path / operation Permission required
Session Roles Anywhere CreateSession Roles Anywhere profile/trust + role trust
Probe STS GetCallerIdentity sts:GetCallerIdentity
Discover store SSO Admin ListInstances sso:ListInstances
Directory read Identity Store ListUsers identitystore:ListUsers
Org spend Cost Explorer GetCostAndUsage (SERVICE) ce:GetCostAndUsage
Budgets Budgets DescribeBudgets budgets:ViewBudget / budgets:DescribeBudgets
Account alias (optional) IAM ListAccountAliases iam:ListAccountAliases

5. License Sync

  • User sync: Yes (IAM Identity Center / Identity Store users)
  • License inventory sync: Yes — infrastructure model (not SaaS seats):
    • AWS Cloud Spend (period total UnblendedCost)
    • Top services by Cost Explorer
    • Configured Budgets (limit + actual spend)
    • Account alias via IAM when permitted (used as scope_label; inventory continues without it)
  • Extra permissions for license sync: Cost Explorer + Budgets + optional iam:ListAccountAliases (see §3.2)
  • CSV fallback: Still available for custom seat/cost spreadsheets

6. Installation In A-Count

  1. Open Settings.
  2. In Provider Connections, click Configure on AWS.
  3. Select Import mode = API.
  4. Set region (Identity Center region) and auth mode:
    • In AWS (EKS/EC2): Pod / instance role, or AssumeRole with Role ARN (blank access key).
    • Outside AWS: Roles Anywhere — paste Role / Profile / Trust Anchor ARNs, client certificate PEM, and private key PEM.
  5. Click Test, then Save.
  6. Run sync from the Sync page (users + license inventory).

7. Dual-mode / CSV

AWS remains dual-mode. If Identity Center is unavailable or Test fails, keep import_mode=csv and upload a user/license spreadsheet. No automatic fallback during API sync.

Configure Canva

Quick summary

  • Required plan: Canva Enterprise (organization). Free, Pro, Teams, and Business do not expose the Admin API.
  • Required role: Organization admin (MFA or SSO required to create clients).
  • Auth: OAuth 2.0 client credentials → paste client_id:client_secret in A-Count.
  • Paste credentials: Settings → Provider Connections → Canva → Configure → API.

Official docs:


1. Plan gate (important)

Canva plan Admin API (A-Count)
Free / Pro No
Teams / Business No (team-level only; no Admin API)
Enterprise (organization) Yes

A-Count uses the Admin API only (not Connect design APIs, not SCIM). License / Free vs Pro plan names are not returned by any Canva API.


2. Create Admin API client

  1. Sign in to Canva as an Organization admin.
  2. Profile → Settings → under Organization settings → Admin API.
  3. Create an API client → name it (e.g. A-Count).
  4. Select scopes (minimum):
    • admin:user:read — required for user sync
  5. Save Client ID and Client secret (secret shown once).
  6. In A-Count: set import_mode = API, paste client_id:client_secret, click Test, then save.
  7. Run user sync.

A-Count calls:

  • POST https://api.canva.com/auth/v1/oauth/token (Basic Auth + grant_type=client_credentials, scope admin:user:read)
  • GET https://api.canva.com/admin/v1/users (pagination via continuation)

3. Data mapped in A-Count

Canva Admin field A-Count field Notes
id provider_user_key Stable Canva user id
email email
display_name / first+last name
SAML name_id username When present; else email local-part
role role admin / brand_designer / member
— status Always active (API has no inactive flag on list)
last_active_at last_activity_at Unix seconds → UTC
Full record raw source: canva_admin_api

Licenses: not available via Admin API (no SKU / Free / Pro / seats inventory). User sync only in V1.


4. Common errors

Symptom Likely cause
401/403 on token Wrong client id/secret, or client missing admin:user:read
401/403 on users Org not Enterprise, or caller is not org admin
Empty user list Client works but org has no listed users

Configure Claude (Anthropic)

Quick summary

  • Default: import_mode = csv (export users from claude.ai admin).
  • API mode: Enterprise only — Compliance Access Key from claude.ai admin.
  • Not supported for user directory: Admin API keys (sk-ant-admin01-...) and inference keys (sk-ant-api03-...).
  • Paste token: Settings → Provider Connections → Claude → Configure → API → API Token.

Official docs:


1. When to use CSV vs API

Plan Recommended mode Why
Team CSV Compliance API returns 403 without Enterprise + Compliance key.
Enterprise API (if Compliance key works) Lists claude.ai members via Compliance API.

If API token test fails, stay on CSV — A-Count does not auto-fallback during sync.


2. Enterprise — Compliance Access Key

  1. In claude.ai admin, request/enable the Compliance API and create a Compliance Access Key (sk-ant-api01-...).
  2. Scopes required for user sync:
    • read:compliance_org_data — list organizations
    • read:compliance_user_data — list users per organization
  3. In A-Count: set import_mode = API, paste the key, click Test token.
  4. Run user sync (POST /users/sync or scheduler).

A-Count calls:

  • GET https://api.anthropic.com/v1/compliance/organizations
  • GET https://api.anthropic.com/v1/compliance/organizations/{org_uuid}/users (paginated with next_page)

3. Data mapped in A-Count

Compliance field A-Count field Notes
id provider_user_key Stable user_01... id
email email Required; rows without email are skipped
full_name name
organization_role role Mapped to admin or user
created_at (not mapped to last_activity) Kept in raw only — no reliable last-activity API yet
Full record raw source: anthropic_compliance_api

Licenses: no per-user license API — use CSV for seat/plan details if needed.


4. Wrong key types (common errors)

Key prefix Result
sk-ant-admin01-... Rejected — Console Admin API, not claude.ai directory
sk-ant-api03-... Rejected — inference API, not Compliance
sk-ant-api01-... Expected for Compliance (if Enterprise enabled)
Compliance key on Team Test/sync 403 → use CSV

Cursor (Admin API)

This page is the operator guide for obtaining a Cursor Admin API key and wiring it into A-Count.

Official reference: Cursor Admin API.


Creating API keys

API keys are created from your team settings. Only team administrators can create and manage API keys.

Admin API

  1. Open cursor.com/dashboard (Cursor web dashboard).
  2. Go to the Settings tab.
  3. Open Advanced (or equivalent advanced / developer section, depending on UI labels).
  4. Find Admin API Keys (sometimes labeled alongside AI Code Tracking API for Enterprise).
  5. Click Create New API Key (or equivalent).
  6. Give the key a descriptive name (e.g. A-Count inventory sync).
  7. Copy the generated key immediately — you will not be able to see the full secret again after leaving the dialog.

Key format: crsr_ followed by a long hex-like string, for example:

crsr_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
        

Scopes: Admin API access is tied to the key created in team settings; use a key that is explicitly allowed for team administration / Admin API (documentation may refer to scopes such as admin:* — follow what the Cursor UI shows when creating the key).


Using the key in A-Count

  1. In A-Count Settings, open the Cursor provider (API mode).
  2. Paste the key into the provider API token field (stored encrypted like other providers).
  3. Run user sync for Cursor.

Authentication (for integrators): HTTP Basic — username = API key, password empty (curl -u 'YOUR_KEY:' …).


Sync behavior (A-Count)

  • Spend first: POST /teams/spend (paginated), then GET /teams/members, so emails/names on spend rows can fill gaps when the members payload omits them.
  • Users: GET /teams/members — teamMembers (also accepts nested data or a members key if present). Member id is resolved from several top-level and nested fields; if the API sends no id but sends email, we use a synthetic stable key email:<lowercased email>.
  • Fallback: If teamMembers is empty but spend returns rows, users are built from spend only (same billing cycle fields in raw).
  • Optional enrichment: If spend fails, members are still returned when the members endpoint supplies data.
  • Last activity (best-effort):
    • GET /teams/groups — first joinedAt seen per email (current + former members, plus unassignedGroup) is stored as activation time (activation_joined_at_ms in raw).
    • POST /teams/filtered-usage-events — paginated team-wide stream (no email filter; default pageSize 1000, CURSOR_USAGE_EVENTS_PAGE_SIZE) over CURSOR_USAGE_LOOKBACK_DAYS (default 90; shorter = fewer pages = faster). Default ~0.06 s minimum between consecutive POSTs (CURSOR_USAGE_EVENTS_REQUEST_INTERVAL_SEC, 0 = off). Optional CURSOR_USAGE_EVENTS_INTER_PAGE_SLEEP_SEC. Set CURSOR_USAGE_EVENTS_DISABLE=1 to skip this endpoint entirely (sync is much faster; last_activity_at then uses only joinedAt from groups). 429 retries: Retry-After when present, else exponential backoff starting at 1.25 s (capped).
    • ProviderAccount.last_activity_at uses the latest usage timestamp when present; otherwise the activation (joinedAt) timestamp. If both calls fail or return nothing for a user, last_activity_at stays unset. Failures on groups or usage do not block the member import.
  • Licenses inventory: not available from the API; /licenses/sync/{id} returns licenses_upserted: 0. License model (v2): P3 catalog-only — single package ref=cursor. API user sync is assign-only; first sighting materializes the package.

Role mapping (→ ProviderAccount.role)

Cursor teamMembers[].role is kept in raw.cursor.member.team_role. Unified role:

  • If isRemoved is true → always user (regardless of API role).
  • Otherwise: member → user; owner, free-owner → admin; admin / administrator → admin; guest → guest.

ProviderAccount.raw

Structured under cursor (see schemas.cursor.CursorTeamMember.raw_payload):

  • source: cursor_admin_api
  • cursor.member: id (string, as returned by Cursor: numeric or opaque e.g. user_…), is_removed, team_role
  • cursor.billing_cycle: subscription_cycle_start_ms, spend_total_members, spend_total_pages (when spend was fetched)
  • cursor.spend_current_cycle: non-null spend fields only (spend_cents, overall_spend_cents, etc.)
  • cursor.last_activity_source: usage_events when last_activity_at comes from filtered usage; team_joined when it falls back to joinedAt from groups; omitted when neither is available
  • cursor.lastActivity: ISO UTC (…Z) for the effective timestamp written to ProviderAccount.last_activity_at (usage preferred over activation)
  • cursor.activation_joined_at_ms, cursor.last_usage_event_ms: raw epoch-ms from Cursor when present

Configure Dropbox Business

Quick Summary

  • Required: A Dropbox Business team (not a personal-only Dropbox account).
  • Required role: Team admin (recommended) so you can create apps and generate access tokens with team scopes.
  • Where to go: Dropbox App Console.
  • What you need: An OAuth access token (short-lived or long-lived) with permission to call the Team API.
  • Paste in A-Count: Settings → Provider Connections → Configure → Import mode = API → API Token.
  • Security: The token is encrypted on the server when saved.

1. Prerequisites

  • Your organization uses Dropbox Business (team accounts).
  • You can create or manage a Dropbox API app for that team.
  • You understand that A-Count calls POST /2/team/members/list — this endpoint only applies to Business teams.

Useful link: - Team: members/list


2. Create a Dropbox app (scoped)

  1. Open the Dropbox App Console and click Create app.
  2. Choose Scoped access (recommended for modern tokens).
  3. Choose Full Dropbox or the access type your security team allows; the critical part is that the token can use Team endpoints.
  4. Under Permissions, enable the team permission needed to list team members (for example members.read — use the permission that matches your app type and Dropbox’s current naming in the console for reading team membership).
  5. Generate an access token for the app (or complete OAuth and copy the access token), with the above permissions granted.

If you are unsure which toggles to enable, refer to Dropbox’s documentation for teams/members/list and enable the listed scopes/permissions.


3. Install in A-Count

  1. Open Settings.
  2. Under Provider Connections, click Configure for Dropbox.
  3. Select API import mode.
  4. Paste the access token into API Token.
  5. Click Test, then Save.

4. What A-Count does

  • User sync: Paginates POST https://api.dropboxapi.com/2/team/members/list (default page size up to 1000) until has_more is false.
  • Fields mapped: Account id, email, name, admin/owner flags, suspended flag, join date, profile photo URL (see DropboxUser.from_dict in backend/schemas/dropbox.py).
  • License inventory sync (POST /api/licenses/sync/{id}): not implemented for Dropbox; use other providers or CSV where applicable.

5. Troubleshooting

“not associated with a team” / “USER_AUTH_NOT_ALLOWED”

  • The token is not a Dropbox Business team token, or it lacks team scopes. Create a Business app token with permissions for team member listing.

Empty or partial member list

  • Confirm the app is installed / linked to the correct Business team.
  • Regenerate the token after changing permissions.

HTTP 401 / invalid token

  • Token expired (short-lived tokens) or revoked — generate a new token and update A-Count.

6. Rotation and security

  • Rotate tokens on a schedule; revoke old tokens in the Dropbox console after switching.
  • A-Count stores the token encrypted and does not display the plaintext after save.

Configure Dropbox Sign (HelloSign)

Quick Summary

  • Required role: Dropbox Sign admin (recommended) on a paid plan (Standard or higher)
  • Where to go: Dropbox Sign Account → API (direct link below)
  • What you need: a Dropbox Sign API Key (HelloSign v3, Basic Auth)
  • Scopes: not applicable (API key does not have OAuth scopes)
  • Installation in A-Count: paste the key into Settings → Provider Connections → Dropbox Sign → Configure → API mode → API Token
  • Security: the token is encrypted when saved server-side

1. Prerequisites

  • Admin access to the Dropbox Sign account (or at least a role that can manage API keys)
  • The account must be on a paid plan
    • Free plans cannot list team members via API
  • Access to the correct team / organization in Dropbox Sign

2. Steps in Dropbox Sign

2.1 Where to go

  • Direct link: https://app.hellosign.com/home/myAccount#api
  • Or UI path: Account → API

2.2 Create / copy the API key

  1. In the API section, locate the API Key.
  2. If you need to generate a new one, create it and give it a clear name (e.g. a-count-sync).
  3. Copy the API key.

If you test with command-line tools on Windows, wrap URLs in quotes when needed.

2.3 Required scopes / permissions

Dropbox Sign uses an API key (Basic Auth) and does not support OAuth scopes for this integration.

Instead, access depends on: - Account plan: listing team members requires a paid plan - Role: admin is recommended to ensure the /team endpoint is accessible

3. Installation in A-Count

  1. Open Settings.
  2. In Provider Connections, click Configure for Dropbox Sign.
  3. Select Import mode = API.
  4. Paste the key into API Token.
  5. Click Test.
  6. Save.

4. What A-Count does (Dropbox Sign)

The integration uses HelloSign v3 endpoints: - GET https://api.hellosign.com/v3/account (validates the key and reads plan hints) - GET https://api.hellosign.com/v3/team (lists team members when allowed)

Notes: - GET /organization is not required for A-Count. On Standard plans it may return 404 Not Found.

5. Known limitations

  • Free plans: team members are not accessible via the API (A-Count will reject the token)
  • Some plans/accounts may return 402 Payment Required on GET /team (API plan required)
  • last_activity_at is not available through the currently used endpoints and will remain null
  • API quota: if your API quota shows 0, team read endpoints may still work, but API actions like sending documents may be blocked by your plan/quota.

5b. License model (v2)

  • Tier: P3 catalog-only (no stable vendor contract_ref from the API)
  • Inventory: heuristic plan deduction from /account + /team (+ optional /organization)
  • Catalog ref: plan slug — free_trial, premium_enterprise, standard_api, standard_web, essentials_api, essentials_web (not the team name)
  • User sync: assign-only to the inventory plan row; overwrites legacy team-name license_ref values

6. Quick troubleshooting

The token is rejected immediately

  • Verify you copied the full API key (no extra spaces).
  • Verify the account is on a paid plan.
  • Verify the key belongs to the correct Dropbox Sign team / organization.

GET /team fails with 402

  • Your plan does not include the required API access for team listing.
  • Upgrade the plan or use another provider mode (if applicable).

Missing / incomplete members

  • Confirm your account role has access to team membership listing (admin recommended).
  • Generate a new API key and test again.

7. Rotation & security

  • Rotate the API key regularly.
  • Revoke the key immediately if it is exposed.
  • A-Count encrypts the token before storing it and does not show it in plain text after save.

Configure ESET

Quick Summary

  • Required role: ESET Business Account / PROTECT Hub Superuser to create a dedicated API user; the API user itself cannot be the Superuser.
  • Where to go: ESET Business Account or ESET PROTECT Hub -> Users -> New user, with Integrations enabled.
  • Auth model: OAuth 2.0 password grant (cloud) or /GetTokens / Inspect authenticate (on-prem). JWT is short-lived; A-Count stores the API username and password, not the JWT.
  • Action: create a dedicated API user, complete the invitation, log in once, then copy username + password into A-Count.
  • Paste into the app: Settings -> Provider Connections -> Configure -> API mode -> connection mode, region or URL, username, password.
  • Security: the password is encrypted on save and stored server-side; never paste it in chat, tickets, or docs.
  • API rights reference: api-rights-reference.md

1. Prerequisites

  • Cloud: ESET PROTECT Cloud (Inspect Cloud optional). The API user must finish account setup and log in once before calling the API.
  • On-prem PROTECT 13+: REST API enabled, dedicated API user with the API permission set (not Administrator, not 2FA).
  • On-prem Inspect: Inspect REST API reachable from the A-Count API host.
  • Cloud and on-prem cannot be mixed on the same connection. Pick one mode in Settings.

2. Auth Model And Storage In A-Count

Item Value
Auth model Cloud: OAuth 2.0 password grant against {region}.business-account.iam.eset.systems. On-prem PROTECT: POST /GetTokens. On-prem Inspect: POST /api/v1/authenticate. Subsequent calls use Authorization: Bearer.
What A-Count stores Provider.api_token stores the encrypted API user password. api_config stores eset_mode, eset_username, eset_region (cloud), eset_base_url (on-prem), and optional eset_verify_ssl.
Encryption ENCRYPTION_KEY for Provider.api_token
Token format in UI API user password only. Username is a separate field.
Regions eu, de, us, ca, jpn

3. Steps In ESET

3.1 Cloud (PROTECT / Inspect)

  1. Sign in as Superuser to ESET Business Account or ESET PROTECT Hub.
  2. Create a dedicated user. Superuser accounts cannot use the API.
  3. Enable Integrations. Grant read access to ESET PROTECT (and Inspect if licensed).
  4. The new user must complete the invitation email and log in once.
  5. In A-Count, choose Cloud, the region of the PROTECT/Inspect tenant, the API username, and the password.

3.2 On-prem PROTECT

  1. Enable the REST API in More -> Settings. Open API ports on the firewall.
  2. Assign a dedicated API user the API permission set. Administrator and 2FA users cannot use the API.
  3. Expose https://<protect-host>:9443 to the A-Count API host (or allow self-signed TLS if needed).
  4. In A-Count, choose On-prem PROTECT, paste the REST URL, username, and password.

3.3 On-prem Inspect

  1. Use a dedicated Inspect API user (not the Administrator account).
  2. In A-Count, choose On-prem Inspect and the Inspect REST base URL.

3.4 Required Scopes / Permissions

Minimum (connection test + device inventory): - Cloud: Integrations + ESET PROTECT read (Device Management GET /v1/devices). - On-prem PROTECT: API permission set that can list devices. - On-prem Inspect: permission to list computers.

License inventory: - Derived from each device's activeProducts (activated ESET products) and, when that list is empty, from deployed security components. Purchased EBA seat totals and unit price are not exposed. Cloud connections also read the installer catalog for product / subscription identity. - Inspect Application Management is probed on cloud connections when licensed. It is not used for user/app matching in this version.

Not in this integration: - User / identity directory sync (ESET has no SaaS member list comparable to SentinelOne console users). - Discovered desktop app attribution (installed-app-per-device is not in the public API).

4. Endpoints A-Count Calls

Capability Method Path / operation Permission required
Cloud auth POST https://{region}.business-account.iam.eset.systems/oauth/token Dedicated API user
On-prem PROTECT auth POST {base}/GetTokens API user, no 2FA
On-prem Inspect auth POST {base}/api/v1/authenticate Inspect API user
Device inventory (cloud / on-prem PROTECT) GET /v1/devices Device Management read
Computer inventory (Inspect on-prem) GET /api/v1/computers Computers read
Installer catalog (cloud) GET /v1/installers Installer Management read (optional)
Inspect Cloud probe (optional) GET /v1/executables?pageSize=1 Application Management read
Directory / user sync N/A Not available N/A
Purchased seat pool / unit price N/A Not in ESET Connect N/A

5. License Sync

  • User sync: No. Combined sync still runs the user job and stores zero accounts.
  • License inventory sync: Yes. Prefer activeProducts (activation + validityDate). If that list is empty, use deployed ESET security products (deployedComponents, excluding Management Agent).
  • Installer catalog: Cloud connections also read GET /v1/installers for product display name, subscription support code, and pool/site label. Installer expireTime is the download-link expiry and is not used as the license renewal date.
  • License row shape: one A-Count license per ESET product name. seats_purchased is managed devices with that product. assigned is devices that checked in within 30 days (ESET Hub-style active units). pricing_model is per_license.
  • Expiration date: product validityDate is stored as the manual renew date when ESET sends it on activeProducts. This tenant often returns an empty activeProducts list, so the date stays blank until ESET populates that field.
  • Cost: ESET Connect does not return unit price or contract value. Leave cost empty or enter it manually.
  • User to license assignment: Not native.

6. Installation In A-Count

  1. Open Settings.
  2. In Provider Connections, click Configure on ESET.
  3. Select Import mode = API.
  4. Choose Cloud, On-prem PROTECT, or On-prem Inspect.
  5. Enter region (cloud) or REST URL (on-prem), API username, and password.
  6. For on-prem self-signed certificates, uncheck SSL verification.
  7. Click Test.
  8. Save.
  9. Optionally check EDR on Provider Connections so this connection is classified as an EDR source for later matching work.

7. Known Limitations

  • No corporate-email user map from ESET APIs. Device-to-user matching is deferred.
  • Public APIs do not return installed third-party applications per device. Do not expect SentinelOne-style discovered apps from this connection.
  • On-prem requires network reachability from the A-Count API host.
  • Cloud JWTs expire in about 60 minutes; A-Count refreshes them automatically.
  • Purchased seat pool, unit price, and EBA portal expiry are not in ESET Connect. Volume is observed devices; enter cost and missing dates manually if needed.

8. Quick Troubleshooting

Authentication Failed

  • Cloud: confirm the user is not Superuser, Integrations is on, and the user has logged in once.
  • Confirm the region matches the PROTECT/Inspect tenant (EU, DE, US, CA, JPN).
  • On-prem: Administrator and 2FA users cannot call the REST API. Use a dedicated API user.

Devices Probe Succeeds, License Inventory Is Empty

  • Devices may lack activeProducts (agent not fully enrolled). Inspect on-prem computers have no product list; A-Count then stores a generic Endpoint row from computer count.

I Do Not See The API Option

  • Restart the backend after upgrade so eset is seeded from APIProviderName.
  • Verify eset is in PROVIDER_REGISTRY.

9. Rotation & Security

  • Rotate the API user password regularly and replace it in A-Count.
  • Keep username, region, and URL in api_config; only the password is treated as a secret.

Configure Figma

Quick summary

  • Default: import_mode = csv (export People from the Figma admin).
  • API mode: SCIM 2.0 (read users) with Admin Tenant ID + SCIM API token.
  • Paste credentials: Settings → Provider Connections → Figma → Configure → API.

Official docs: Figma SCIM API.


1. When to use CSV vs API

Situation Recommended mode Why
No Organization/Enterprise SCIM CSV SCIM requires Org/Enterprise + SAML SSO
SCIM enabled in Admin API Lists SCIM-bound users + seat types

If API token test fails, stay on CSV — A-Count does not auto-fallback during sync.

Note: Figma SCIM returns users bound to the SCIM directory. Orgs without SCIM provisioning should keep CSV.


2. API setup

  1. In Figma Admin → Settings → Login and provisioning:
    • Configure SAML SSO (required before SCIM).
    • Copy Tenant ID.
    • Enable SCIM and generate an API token (shown once).
  2. In A-Count:
    • Set import_mode = API
    • Figma Tenant ID: paste Tenant ID (or a full https://www.figma.com/scim/v2/{tenant}/… URL)
    • Credentials: paste the SCIM API token
    • Click Test, then save
  3. Run user sync (POST /users/sync or scheduler) — license inventory aggregates seat types first.

A-Count calls:

  • GET https://www.figma.com/scim/v2/{tenant_id}/Users?startIndex=&count= with Authorization: Bearer {scim_token}

3. Data mapped in A-Count

Figma SCIM field A-Count field Notes
id provider_user_key Stable Figma user id
userName / emails email Prefer userName when it looks like an email
displayName / name name
figmaAdmin role admin when true; else user
active status false → deleted
roles[seatType] raw.license_type Figma Full / Dev / Collab / View
Full record raw source: figma_scim_api

Licenses: active users are counted by seat type into inventory packages (Figma Full, …). Purchased capacity / USD is not exposed on SCIM.


4. Common errors

Symptom Likely cause
401/403 Wrong SCIM token, or SCIM not enabled
404 Wrong Tenant ID
Sync OK but few users Only SCIM-provisioned users are returned — use CSV for full People export
Missing Tenant ID Required in API mode (figma_tenant_id)

Configure GitHub

Quick Summary

  • Required role: GitHub Organization Owner or Admin.
  • Where to go: GitHub App settings + your GitHub Organization settings.
  • What you need: A GitHub App (App ID + private key) installed on your Organization, and your Organization slug.
  • Extra field in A-Count: GitHub Organization (ex: my-company).
  • Security: The private key is sent once, encrypted and stored server-side.

1. Prerequisites

  • You must have administrative rights on the GitHub Organization.
  • Know your Organization Slug: it’s the name in the URL (ex: github.com/my-company -> slug is my-company).
  • A-Count uses GitHub App installation tokens (short-lived) to call the GitHub REST API. Personal Access Tokens (PATs) are disabled in this repository’s GitHub provider UI/backend.

2. Create a GitHub App

Create a GitHub App in your GitHub settings and install it on your Organization.

2.1 Create the App + private key

  1. Open Registering a GitHub App (GitHub Developer settings → GitHub Apps → New GitHub App).
  2. Name it clearly, for example a-count. Webhook and Setup URL can stay empty for this provider.
  3. Generate a private key and download the PEM. See Managing private keys for GitHub Apps.
  4. Note the App ID.

2.2 Permissions

Set these on the App before the first install. Names below are the labels in the GitHub App permission screen. Endpoint-to-permission mapping: Permissions required for GitHub Apps. How to pick them: Choosing permissions for a GitHub App.

Organization permissions:

GitHub label Access What A-Count calls
Members Read and write List members. Write is required to remove a member or outside collaborator from Anomalies. Teams are covered by Members (there is no separate Teams permission on the installation token).
Administration Read-only GET /orgs/{org} metadata
Plan Read-only plan on GET /orgs/{org}, stored as the organization license (api_config.github_license)
GitHub Copilot Business Read-only GET /orgs/{org}/copilot/billing and GET /orgs/{org}/copilot/billing/seats. Without it, the organization plan still syncs and existing Copilot seats are left in place.
Organization Copilot metrics Read-only GET /orgs/{org}/copilot/metrics/reports/organization-28-day/latest and …/users-28-day/latest. See the metrics policy below.

Repository permissions (needed for last activity on private repositories; activity scans are on by default):

GitHub label Access What A-Count calls
Metadata Read-only Repository list
Contents Read-only Commits in accessible repositories

Copilot usage metrics policy. The App permission is not enough. An enterprise owner must set Copilot usage metrics to Enabled everywhere (or allow the organization to enable it). Steps: Managing policies for Copilot in your enterprise and Copilot policies. API reference: Copilot usage metrics. A-Count does not import those 28-day reports yet. Seat last use comes from the billing seats endpoint. Grant the permission and enable the policy now so the reports answer when that reader is added. Billing and seats reference: Copilot user management.

After any permission change, re-install / accept the permission request on each organization (Installing your own GitHub App). Without Members Write, sync still works but remote remove returns HTTP 403 (insufficient_scopes).

SCIM note: A-Count does not call SCIM DELETE. If the org is IdP/SCIM-provisioned, a removed member may be re-created by the IdP — treat that as an attention point outside this feature.

Remote remove (Anomalies): Admins can remove a GitHub member or outside collaborator from the org via Anomalies (delete-family proposals). The product UI always performs a real remove (reason + danger confirmation). The API also accepts dry_run=true on POST /api/remote-actions/remove-user for operators/tests only — it is not exposed in the client UI.

2.3 Install the App on the organization

Install the app on the target organization (Installing your own GitHub App). Choose All repositories when last activity should include private repos. Selected repositories only contribute activity for the repos you pick.

3. Configure in A-Count (Settings)

  1. Open Settings in A-Count.
  2. Under Provider Connections, click Configure for GitHub.
  3. Fill in GitHub organization (slug).
  4. Fill in:
    • GitHub App ID
    • Private key (PEM)
  5. (Optional) Configure identity scan options (commit-scan limits and preferred domain).
  6. Save GitHub App setup.
  7. Use Fetch org plan to verify the app can read organization metadata.

4. Member emails and API limits (important)

A-Count’s API sync loads each member’s public profile (GET /users/{login}). GitHub only returns an email there if the user exposes an email on their profile. Hidden or org-only addresses are not returned on that endpoint.

Expectation Reality
“My GitHub App/token can read emails → I get everyone’s work email” GitHub does not expose private member emails via the public profile endpoint. Most org members will have email = null unless they made it public.
“Same as the GitHub Enterprise member CSV” Enterprise exports often include SAML / IdP fields that REST member sync does not fetch unless you add separate integrations (see below).

If most emails are empty after sync, that is usually expected, not a misconfiguration. Correlation with your IdP then depends on another identifier or a richer data source.

Practical ways to get corporate emails (or SAML fields) into A-Count: 1. CSV import — Export members from GitHub Enterprise / your IdP / HR with login, email, roles, etc., then use A-Count’s GitHub CSV/staged import so rows carry the addresses you already trust. 2. GitHub Enterprise Cloud + SCIM (optional in A-Count) — SCIM provisioned identities can supply IdP emails, externalId / userName (stored as saml_name_id), and a SCIM roles count. See §6 (SCIM enrichment).\n\n Note: SCIM access often requires enterprise identity permissions that may not be available to GitHub App installation tokens. 3. Process — Keep API sync for live membership and roles; enrich/reconcile emails periodically from CSV or another system of record.

5. What A-Count does

  • User Sync: A-Count calls the GitHub API to list all members of the specified organization.
  • Two-factor authentication: If GitHub allows the filter=2fa_disabled members list for the authenticated identity, A-Count infers tfa_enabled / tfa_level. Otherwise 2FA stays unknown.
  • Data Collected: login, name, email (if public/available), org role, plus a best-effort last_activity_at derived from public signals, GraphQL contributions (GITHUB_ACTIVITY_GRAPHQL_CONTRIBUTIONS, enabled by default), optional search (GITHUB_ACTIVITY_ORG_SEARCH), a deep accessible-org-repo commit scan (GITHUB_ACTIVITY_DEEP_REPO_SCAN, enabled by default), accessible repo event feeds (GITHUB_ACTIVITY_REPO_EVENTS_SCAN, enabled by default), and optional targeted commit search by author (GITHUB_ACTIVITY_COMMIT_SEARCH_BY_AUTHOR, disabled by default). Org-level activity maps (and teams) are built in parallel, then merged per login (latest timestamp wins). Deep commit / repo-events scans also walk repos concurrently (GITHUB_ACTIVITY_REPO_SCAN_CONCURRENCY, default 8). Tunables live in backend/.env.example.
  • License Tracking: A-Count reads organization metadata (GET /orgs/{org}) to infer a plan snapshot and persists it under api_config.github_license (the plan only). When GET /orgs/{org}/copilot/billing succeeds, that snapshot is stored under api_config.github_copilot and inventory creates a second license on the same organization (ref copilot_business or copilot_enterprise, seats = seat_breakdown.total, including 0). GET /orgs/{org}/copilot/billing/seats links that license through the shared add-on license_usages writer, on one account per login (not one row per team). A failed billing read keeps the stored github_copilot snapshot, including a one-time copy of a legacy github_license.copilot object. Copilot last activity is stored on the license_usages row (last_activity_at, activity_editor) and does not replace GitHub last_activity_at. Account lists return it in license_activities for that license, including the time. The Copilot license column shows that seat date. The organization-plan column shows GitHub last_activity_at only (commits and events). Profile updated_at and repository pushed_at are not last activity. The two values are not copied onto each other. Its raw includes tags: ["ia"] and pricing_model per_usage (one usage amount on the package, not a price times seats). Each license refresh sets cost to the current calendar month's Copilot netAmount and currency to USD. The usage note is that month's first day through today (YYYY-MM-DD – YYYY-MM-DD), rewritten on each refresh. Usage min and usage max stay empty. A Copilot 403 or 404 does not block the plan license and does not remove seats already stored.
  • License model (v2): catalog ref = plan slug (plan_name_raw / tier, e.g. team). Org slug is not a catalog ref (it is a P2 workspace scope). User sync stamps raw.license_ref from the plan snapshot and assign-only links to the existing inventory row.

6. SCIM enrichment (optional, server-side)

For GitHub Enterprise Cloud organizations with SCIM provisioning, A-Count can attempt to enrich members with SCIM fields when GITHUB_SCIM_ENRICHMENT=1 is set on the API host (see backend/.env.example).

When enabled, A-Count calls GET /scim/v2/organizations/{org}/Users (paginated) after listing members. It merges:\n- missing email\n- saml_name_id (prefers externalId, else userName)\n- organization_roles_count from SCIM roles.

If SCIM is unavailable (403/404), sync still succeeds; check logs for GitHub SCIM:.

7. Troubleshooting

  • 401 Unauthorized: The installation token request failed (check App ID / PEM) or the GitHub API rejected the token.
  • 403 Forbidden: The GitHub App is missing permissions, or it is not installed on the organization. If only Search / last_activity fails while members sync works, the app likely lacks repository access to the repos you care about.
  • Empty Email Fields: Very common with API sync—see Member emails and API limits above. Use a CSV import or enterprise identity APIs if you need corporate addresses in A-Count.
  • Organization not found: Double-check the slug. It must be the organization “login” name, not the display name.

Configure Google

Quick Summary

  • Required role: Google admin
  • Where to go: Go to Google Cloud Console
  • Enable in that Cloud project: Admin SDK API and Enterprise License Manager API — required for license sync
  • What you need: an OAuth client credentials JSON file
  • Important: Google does not use the API Token field in A-Count
  • Recommended: upload the client JSON and complete browser sign-in from Settings → Configure Google (admin API routes under /api/providers/google/workspace/)
  • Server storage: OAuth client JSON + authorized-user token are stored encrypted in the database on the Google provider row.
  • Required OAuth scopes:
    • https://www.googleapis.com/auth/admin.directory.user.readonly
    • https://www.googleapis.com/auth/apps.licensing
    • https://www.googleapis.com/auth/admin.directory.user.security
    • https://www.googleapis.com/auth/admin.directory.group.readonly (IdP groups page)
  • Optional backend environment value: GOOGLE_CUSTOMER_ID

1. Prerequisites

  • Be a Google admin
  • Have access to a Google Cloud project
  • Be allowed to enable Google APIs
  • Be able to create OAuth client credentials

2. Required APIs And Scopes

The backend reads: - Workspace users through the Admin SDK Directory API - Workspace license assignments through the Enterprise License Manager API

In the Google Cloud project used for the OAuth client, enable these APIs (API Library): - Admin SDK API - Enterprise License Manager API — open this page, click Enable, then wait a few minutes. Required for license sync; without it, sync fails with 403 accessNotConfigured / “Enterprise License Manager API has not been used in project … or it is disabled”

If you enabled an API recently, wait a few minutes for propagation, then retry the Google sync.

The OAuth credentials must allow these scopes: - https://www.googleapis.com/auth/admin.directory.user.readonly - https://www.googleapis.com/auth/apps.licensing - https://www.googleapis.com/auth/admin.directory.user.security - https://www.googleapis.com/auth/admin.directory.group.readonly (directory groups for Users → IdP groups)

After adding the group scope, reconnect Google Workspace from Settings so the stored token includes it.

3. Create The OAuth Credentials

  1. Go to Google Cloud Console.
  2. Go to Google API Library and enable Admin SDK API and Enterprise License Manager API (see §2).
  3. Go to Credentials and create an OAuth client.
  4. Download the credentials JSON file.
  5. Upload it from A-Count Settings → Configure Google.

Useful links: - Go to Google Cloud Console - Go to Google API Library - Go to Credentials

4. Extra Stored Fields

Google does not use a provider API token stored in the database.

Instead, the backend persists OAuth material encrypted on the Google Provider row:

  • provider.google_oauth_client_json_encrypted: OAuth client from Google Cloud (installed or web section).
  • provider.google_oauth_token_json_encrypted: access + refresh tokens after admin consent.

Optional environment variables:

  • GOOGLE_CUSTOMER_ID: Workspace customer ID (C0xxxxxxx). If unset, inferred from Directory API when possible.
  • OIDC_REDIRECT_BASE (required at startup): public API origin; Google Workspace OAuth callback is {OIDC_REDIRECT_BASE}/api/providers/google/workspace/oauth/callback. Must match an Authorized redirect URI on the Google OAuth client in Google Cloud.
  • FRONTEND_ORIGIN (required at startup): browser SPA origin; after successful consent the API redirects to {FRONTEND_ORIGIN}/settings?google_oauth=success (or &google_oauth=success).
  • Legacy GOOGLE_OAUTH_REDIRECT_URI / GOOGLE_OAUTH_SUCCESS_REDIRECT env vars are ignored; use OIDC_REDIRECT_BASE and FRONTEND_ORIGIN only.
  • MASTER_KEY: required to encrypt and decrypt the stored OAuth client JSON and authorized-user token.
  1. Open Settings as an admin.
  2. Under Provider Connections, click Configure for Google.
  3. Choose API synchronization.
  4. In Google Workspace connection:
    • Click Upload client JSON and select the OAuth client file downloaded from Google Cloud.
    • Click Connect with Google; complete consent in the new browser tab (Workspace admin).
  5. After consent, the API redirects to {FRONTEND_ORIGIN}/settings?google_oauth=success (toast in Settings).
  6. Click Save changes on the dialog if you changed import mode.

Ensure the OAuth client in Google Cloud lists the same redirect URI as {OIDC_REDIRECT_BASE}/api/providers/google/workspace/oauth/callback.

6. What A-Count Does

  • Browser flow (Settings): GET /api/providers/google/workspace/oauth/start (admin) returns a Google authorization URL; GET /api/providers/google/workspace/oauth/callback exchanges the code and stores the authorized-user token encrypted in the database.
  • Runtime auth: _ensure_credentials in data_providers/google.py loads the encrypted token from the Google provider row, requires a refresh_token, and writes refreshed tokens back to the database. There is no InstalledAppFlow.run_local_server fallback (unsuitable for Docker/production).
  • Directory API lists Workspace users; Licensing API lists license assignments.
  • customerId can be inferred from Directory API; GOOGLE_CUSTOMER_ID (or provider api_config.google_customer_id) overrides when needed.
  • License inventory contract_ref (P2 scope) = Workspace customerId (C0xxxxxxx). The Licensing API omits it on assignment items; the backend stamps the resolved request customerId onto each item before convert.

7. Important Difference

  • Google does not follow the same token model as Slack, Monday, GitHub, or Miro.
  • There is no Provider.api_token used for Google today.
  • The real source of authentication is the encrypted pair client JSON + authorized-user token on the Google provider row.

8. Quick Troubleshooting

  • 412 Precondition Failed on Google user or license sync: the stored token is missing, has no refresh_token, or cannot be refreshed. Use Disconnect then Connect with Google in Settings (admin).
  • redirect_uri_mismatch: add the exact callback URL under Authorized redirect URIs for the OAuth client in Google Cloud (Web application type recommended): {OIDC_REDIRECT_BASE}/api/providers/google/workspace/oauth/callback (no trailing slash). Remove any legacy GOOGLE_OAUTH_REDIRECT_URI from .env — it is ignored.
  • OAuth flow does not start: upload the client JSON from Settings and ensure MASTER_KEY is stable and configured.
  • Invalid or stale credentials: use Disconnect in Settings, then Connect with Google again.
  • 403 accessNotConfigured / Enterprise License Manager API disabled: open Enterprise License Manager API on the same Cloud project as the OAuth client, click Enable, wait a few minutes, then re-run sync. Scope alone is not enough if the API is off.
  • Missing licenses: verify that the apps.licensing scope is granted and that Enterprise License Manager API is enabled.
  • Missing users: verify that the admin.directory.user.readonly scope is granted.
  • Licensing API error about customer ID: set GOOGLE_CUSTOMER_ID explicitly if automatic inference is not enough.
  • Licenses sync but no contract / empty contract_ref: re-run Google license inventory after this enrichment fix; expected contract id is the Workspace customerId (C0…), not a commercial deal number (Google Licensing has no deal id).

Configure Google Cloud (GCP)

Quick Summary

  • Required role: Organization or project IAM admin (to create a service account and grant read roles)
  • Where to go: Google Cloud Console
  • Auth model: Service account JSON (not an API token, not Google Workspace OAuth)
  • Action: create a service account, download its JSON key, grant read-only IAM roles, upload the JSON in A-Count
  • Paste into the app: Settings → Provider Connections → Google Cloud → Configure → API mode → upload service account JSON
  • Separate from Google (Workspace): GCP does not use the Workspace OAuth connection
  • Security: the JSON key is encrypted with MASTER_KEY and stored on the gcp provider row
  • API rights reference: api-rights-reference.md
  • License model (v2): single catalog row ref=gcp (per-company). API user sync is assign-only to that row; no vendor SKU inventory. Org id may later be a P2 contract_ref.

1. Prerequisites

  • A Google Cloud organization or at least one project you can administer
  • Permission to create service accounts and grant IAM roles
  • Billing is not required for V1 (A-Count does not sync Cloud Billing spend yet; license cost stays manual)

2. Auth Model And Storage In A-Count

Item Value
Auth model Google service account (JSON key)
What A-Count stores Provider.gcp_service_account_json_encrypted
Non-secret config api_config.gcp_organization_id (optional, e.g. organizations/123456789)
Encryption MASTER_KEY
UI Dedicated upload on the Google Cloud configure dialog (not the generic API Token field)

3. Steps In Google Cloud

3.1 Create A Project (If Needed)

  1. Open Google Cloud Console.
  2. Create or select a project that will own the service account (any project in your org is fine).

3.2 Enable APIs

In APIs & Services → Library, enable:

API Why
Cloud Resource Manager API List projects
Cloud Asset API (recommended) Org-wide IAM principal search (faster)
IAM API Policy reads when Asset API is unavailable
Policy Analyzer API Service account last authentication (day-level)
Cloud Logging API Human IAM user activity from Cloud Audit logs

Direct links (replace PROJECT_ID): - https://console.cloud.google.com/apis/library/cloudresourcemanager.googleapis.com?project=PROJECT_ID - https://console.cloud.google.com/apis/library/cloudasset.googleapis.com?project=PROJECT_ID - https://console.cloud.google.com/apis/library/iam.googleapis.com?project=PROJECT_ID - https://console.cloud.google.com/apis/library/policyanalyzer.googleapis.com?project=PROJECT_ID - https://console.cloud.google.com/apis/library/logging.googleapis.com?project=PROJECT_ID

3.3 Create The Service Account

  1. Go to IAM & Admin → Service Accounts.
  2. Click Create service account.
  3. Name it clearly (e.g. a-count-gcp-readonly).
  4. Skip optional user access unless your org requires it.
  5. Open the new service account → Keys → Add key → Create new key → JSON.
  6. Download the JSON file and store it securely (A-Count will keep an encrypted copy after upload).

3.4 Grant IAM Roles (Minimum Read Access)

Grant these roles on the organization (preferred) or on every project you want inventoried:

Minimum (recommended custom or predefined):

Role Purpose
roles/browser Browse projects/folders in Resource Manager
roles/iam.securityReviewer Read IAM policies (getIamPolicy)
roles/iam.serviceAccountViewer List all service accounts via IAM API (full SA inventory)
roles/cloudasset.viewer Org-wide IAM search via Cloud Asset Inventory (highly recommended)
roles/policyanalyzer.activityAnalysisViewer Service account last authentication (Policy Analyzer)
roles/logging.viewer Human IAM user activity from Cloud Audit logs

How to grant at organization level: 1. Open IAM for your organization (select the organization in the resource picker, not a single project). 2. Grant access → paste the service account email (…@….iam.gserviceaccount.com). 3. Assign the roles above → Save.

If you only grant roles on individual projects, A-Count will still sync principals for those projects, but coverage will be incomplete.

  1. In Cloud Console, open the resource picker → select your organization.
  2. Note the numeric Organization ID.
  3. In A-Count, paste either 123456789012 or organizations/123456789012.

4. Endpoints A-Count Calls

Capability Method Operation Permission / API
List projects POST/GET Cloud Resource Manager projects.search (state:ACTIVE) Resource Manager + Browser
Principals (preferred) GET Cloud Asset searchAllIamPolicies on organizations/{id} Cloud Asset Viewer
Principals (fallback) POST Resource Manager getIamPolicy on org + each project Security Reviewer
Service accounts GET IAM projects.serviceAccounts.list per project Service Account Viewer
SA last activity GET Policy Analyzer serviceAccountLastAuthentication/activities:query per project Activity Analysis Viewer
Human last activity POST Cloud Logging entries:list (Cloud Audit logs) per project Logging Viewer
Connectivity test same as list projects Lightweight projects.search Browser

OAuth scope used by the service account client: https://www.googleapis.com/auth/cloud-platform. The narrower cloud-platform.read-only scope is not accepted by the IAM API (serviceAccounts.list) or the Cloud Asset API and causes 403 PERMISSION_DENIED (ACCESS_TOKEN_SCOPE_INSUFFICIENT); effective access remains read-only through the granted IAM roles.

5. What A-Count Syncs

GCP source A-Count field Notes
IAM user: / serviceAccount: members ProviderAccount Groups and special identities are skipped
IAM Service Accounts API ProviderAccount All project SAs, including those without IAM bindings
Member email email, provider_user_key Key = lowercased email
Principal type employee_type user or service_account (canonical)
SA metadata raw.gcp.*, raw.is_service_account roles, projects, discovery source, disabled state
SA last activity last_activity_at, raw.gcp.last_activity_source Policy Analyzer (policy_analyzer); day-level granularity
Human last activity last_activity_at, raw.gcp.last_activity_source Cloud Audit logs (cloud_logging); best available timestamp in lookback window
Synthetic package License Google Cloud Platform Infrastructure seat rule; cost is manual

Last activity semantics

Principal Source What it measures
Service account Policy Analyzer Last time the SA authenticated to a Google Cloud API (day-level; may lag)
Human IAM user Cloud Audit logs Last GCP API/console authentication seen in audit logs within the lookback window

This is not the same as Google Workspace last login (lastLoginTime on the Google provider), which reflects Workspace sign-in only.

Optional api_config keys (non-secret):

Key Default Purpose
gcp_activity_enabled true Enable last-activity enrichment during sync
gcp_activity_lookback_days 90 Cloud Logging scan window for human users
gcp_activity_max_log_pages 50 Global page cap for org-level Cloud Logging scans
gcp_activity_max_log_pages_per_project 10 Per-project fallback when org-level logging is unavailable
gcp_activity_auto_disable_above_projects 500 Auto-skip activity when project count exceeds this (unless gcp_activity_enabled is explicitly set)
gcp_max_per_project_scans 250 Max projects scanned for SA inventory / Policy Analyzer
gcp_full_sa_inventory false When true, scan all org-listed projects for SAs (slow in large orgs)
gcp_per_project_concurrency 10 Parallel workers for per-project API calls (SA inventory, Policy Analyzer, and human activity per-project Logging fallback)
gcp_folder_id — Optional folder scope instead of org (e.g. folders/123)

Project scope: When Organization ID is set, project listing uses parent:organizations/{id} — not every project visible to the service account globally. Per-project APIs (SA inventory, Policy Analyzer) default to IAM-relevant projects only (projects where principals have bindings). Set gcp_full_sa_inventory: true to scan all org projects for service accounts without IAM bindings.

Performance (large orgs): - Human last-activity prefers a single org-level Cloud Logging query; the per-project fallback runs with the same bounded concurrency as Policy Analyzer (not one project after another). - Service-account creator lookup (owner enrichment during sync) prefers the SA home project from the email (…@PROJECT.iam.gserviceaccount.com), caps extra projects, and records lookup_failed on miss so the next sync does not re-scan Logging for the same account. This does not disable last-activity enrichment.

Limitations: - Service account timestamps are day-level only and may lag behind real usage. - Human activity depends on audit log retention (often 30–400 days by log type); principals with no audit entries keep last_activity_at empty. - Large orgs: human activity uses a single org-level Cloud Logging query when Organization ID is set; per-project scans are scoped and capped.

6. License Sync

  • User sync: Yes (API mode) — IAM principals → accounts
  • License inventory sync: Yes — ensures one package Google Cloud Platform (pricing_model: per_company)
  • Cost / billing: Manual in the Licenses UI (Cloud Billing export is not wired in this version)

7. Installation In A-Count

  1. Open Settings.
  2. Under Provider Connections, find Google Cloud (not Google) → Configure.
  3. Select Import mode = API.
  4. Upload the service account JSON.
  5. Optionally set Organization ID (strongly recommended — limits project listing to your org).
  6. Click Test (lists reachable active projects).
  7. Save, then run Sync from the Sync page (or wait for the scheduler).

8. Known Limitations

  • Does not replace Google Workspace user/license sync
  • Does not import Cloud Billing invoices or spend (add cost manually on the license)
  • IAM group members are not expanded to individual users
  • Without roles/iam.serviceAccountViewer, only service accounts with IAM bindings are imported
  • Without organization-level roles / Asset API, only projects the SA can see are scanned
  • Very large orgs: per-project getIamPolicy fallback can be slow; prefer Cloud Asset Viewer on the org
  • Last activity requires Policy Analyzer + Logging APIs enabled and the roles above; sync still completes if those APIs return 403

9. Quick Troubleshooting

Test fails: “service account JSON is not configured”

  • Upload the JSON again from Settings → Google Cloud.

403 / permission denied on projects

  • Enable Cloud Resource Manager API on the service account’s project.
  • Grant roles/browser (and Asset Viewer) on the organization.

Sync returns zero principals

  • Confirm the SA has roles/iam.securityReviewer or Cloud Asset Viewer.
  • Confirm IAM policies actually contain user: / serviceAccount: members (not only groups).
  • Set Organization ID in A-Count so Asset Inventory can be used.

Confused with Google Workspace

  • Google = Workspace Directory + Licensing (OAuth admin).
  • Google Cloud = GCP Resource Manager + IAM (service account JSON).

10. Rotation & Security

  • Rotate the service account key regularly (create a new key, upload it, then delete the old key in GCP).
  • Prefer organization-scoped read-only roles; never grant Owner/roles/editor to the A-Count SA.
  • Revoke and delete keys that are no longer used.
  • Credentials are encrypted before storage and are not shown in plain text after save.

Configure Grammarly

Quick summary

  • Default: import_mode = csv (export users from Grammarly Admin).
  • API mode: Enterprise (or Grammarly for Education institution-wide) — License Management API via OAuth 2.0 credentials.
  • Paste token as client_id:client_secret: Settings → Provider Connections → Grammarly → Configure → API.

Official docs:


1. When to use CSV vs API

Plan Recommended mode Why
Business / Pro / personal CSV License Management API is not documented for these plans
Enterprise / Education (institution) API (if OAuth credentials UI is available) Lists users, invitees, last activity, seat summary

If the Admin panel has no OAuth 2.0 credentials menu, stay on CSV and ask Grammarly support to enable License Management API access. A-Count does not auto-fallback during sync.


2. Enterprise — OAuth credentials

  1. Sign in as a Grammarly org admin → Admin panel.
  2. Open Organization → Configurations → OAuth 2.0 credentials.
  3. Add credential → enable License Management API (users-api:read is enough for sync; users-api:write is not used by A-Count yet).
  4. Copy Client ID and Client Secret.
  5. In A-Count: set import_mode = API, paste client_id:client_secret, click Test, then save.
  6. Run user sync (and license sync for seat inventory).

A-Count calls:

  • POST https://auth.grammarly.com/v4/api/oauth2/token (grant_type=client_credentials, scope users-api:read)
  • GET https://api.grammarly.com/ecosystem/api/v1/users
  • GET https://api.grammarly.com/ecosystem/api/v1/invitees
  • GET https://api.grammarly.com/ecosystem/api/v1/institutions-summary (seat inventory)

3. Data mapped in A-Count

Grammarly field A-Count field Notes
user_id / invitee id provider_user_key Invitees use invitee:{id}
email email Required for matching
name name
is_admin role admin or user
last_activity_at last_activity_at Users only; invitees use status
Invitee status status INVITED → pending, EXPIRED → expired
Full record raw source: grammarly_license_management_api

Licenses: institutions-summary seats / user_types → org license inventory (name=Grammarly, scope_label = institution name, scope_id = institution id).


Configure HiBob (Bob)

Quick Summary

  • Required role: HiBob admin (or a role that can create Service Users and Service User tokens).
  • Where to go: HiBob Settings → Integrations → API access (wording may vary slightly by HiBob UI version).
  • What you need: A Service User with an API ID and Token (HiBob’s HTTP Basic model).
  • Paste in A-Count: Settings → Provider Connections → Configure → Import mode = API → API Token.
  • Security: The credential is encrypted on the server when saved.

1. Prerequisites

  • Active HiBob tenant with API access enabled.
  • A Service User allowed to call POST /v1/people/search.

Official reference (verify against your HiBob plan and UI): - HiBob API authentication


2. Build the value for “API Token”

HiBob expects HTTP Basic authentication. A-Count accepts either form:

  1. Full header value — paste exactly what you would put after Authorization:, for example: Basic <base64(serviceUserId:token)>
  2. Base64 payload only — paste only <base64(serviceUserId:token)> (without the word Basic); A-Count will prepend Basic automatically.

Where: - serviceUserId = HiBob Service User ID - token = HiBob Service User Token - base64(serviceUserId:token) = Base64 encoding of the string serviceUserId:token (a single colon between id and token).

Do not paste the raw id:secret without Base64 unless you already prefixed it as Basic … per form (1).


3. Install in A-Count

  1. Open Settings.
  2. Under Provider Connections, click Configure for HiBob.
  3. Select API import mode.
  4. Paste the API Token value as described in section 2.
  5. Click Test, then Save.

4. What A-Count does

  • Single request: POST https://api.hibob.com/v1/people/search with JSON body:
    • showInactive: true
    • humanReadable: "REPLACE"
    • fields: root.id, root.fullName, root.email, root.surname, internal.status, internal.lifecycleStatus, work.title, work.department, employee.orgLevel, payroll.employment.contract, payroll.employment.type
  • User sync: Maps each returned employee into internal user records (converter in backend/services/user_service.py — HiBobUserConverter).
  • License inventory sync (POST /api/licenses/sync/{id}): not implemented for HiBob; this integration is directory / HR people oriented, not SaaS seat inventory.

5. Troubleshooting

400 Bad Request (often Illegal base64 character)

  • You pasted the raw serviceUserId:token pair with a Basic prefix, or an otherwise malformed Authorization value.
  • Fix: paste either the raw pair without Basic (A-Count encodes it), or paste only the Base64 payload / full Basic <base64> header value.
  • Example raw pair: SERVICE-35854:your-secret-token (54 characters is typical).

401 Unauthorized

  • Wrong Service User ID/token pair, wrong Base64, or token revoked. Regenerate in HiBob and rebuild the Basic value.

403 Forbidden

  • Service User lacks permission for People / Employee read APIs. Adjust Service User permissions in HiBob.

Empty or very small employee list

  • Confirm showInactive behavior matches your expectation; inactive people are included by design (showInactive: true).
  • Check field-level access in HiBob for the Service User.

6. Rotation and security

  • Rotate Service User tokens on a schedule; revoke the old token in HiBob after updating A-Count.
  • A-Count stores the secret encrypted and does not show it in plaintext after save.

Configure HubSpot

Quick Summary

Auth Private App access token (Bearer)
Required scope settings.users.read
Required role HubSpot Super Admin
Endpoint GET https://api.hubapi.com/settings/v3/users
User sync Yes
License sync No
Category marketing_automation
Billing seat rule assigned_until_removed

1. Prerequisites

  • A HubSpot account on a Professional or Enterprise plan (Private Apps require a paid plan).
  • Super Admin permissions in HubSpot (required to create Private Apps).

2. Create a Private App in HubSpot

  1. Log in to HubSpot and open Settings (gear icon).
  2. In the left sidebar, navigate to Integrations → Private Apps.
  3. Click Create a private app.
  4. Give it a name (e.g. A-Count Integration) and an optional description.
  5. Go to the Scopes tab.
  6. Under CRM, find and enable settings.users.read.
  7. Click Create app and confirm.
  8. Copy the access token — HubSpot only shows the full token once. Store it securely.

3. Configure in A-Count

  1. Open Settings → Provider Connections.
  2. Find HubSpot and click Configure.
  3. Select API Sync mode.
  4. Paste the access token into the API Token field.
  5. Click Test to verify the token works.
  6. Click Save.

Once configured, HubSpot users will be synced automatically during scheduled syncs.

4. What Gets Synced

Field Source
Email email from Settings Users API
Name firstName + lastName
Role admin if superAdmin is true, otherwise user
Status Always active (the API only returns active portal users)
Provider user key HubSpot user id

5. CSV Fallback

HubSpot also supports CSV upload as a fallback. To use CSV mode:

  1. In HubSpot, go to Settings → Users & Teams and export the user list.
  2. Ensure each row has at least an email column.
  3. In A-Count, set import mode to CSV and upload the file.

CSV format:

email,name,role,status,last_activity_at
        jane@company.com,Jane Doe,user,active,2026-06-15
        john@company.com,John Smith,admin,active,2026-06-20
        

6. Known Limitations

  • The Settings Users API only returns active portal users; pending-invitation users are not included.
  • No per-user license/SKU data is available from HubSpot's API.
  • last_activity_at is not exposed by the Settings Users API.
  • Rate limits: HubSpot enforces 100 requests per 10 seconds per Private App. A-Count handles 429 responses with automatic retry.

7. Troubleshooting

Error Cause Fix
401 Unauthorized Token is invalid, expired, or was regenerated Create a new Private App token and reconfigure in Settings
403 Forbidden Token lacks the settings.users.read scope Edit the Private App scopes in HubSpot and enable settings.users.read
No users returned Account has no active users or wrong HubSpot portal Verify you are using the token from the correct HubSpot portal

Configure JetBrains

Quick Summary

  • Required role: JetBrains organization admin (to enable API access and manage tokens) or another role allowed by JetBrains (team admin / primary billing contact).
  • Where to go: JetBrains Central Console → Org administration → API.
  • Auth model: API key headers (X-Api-Key, X-Customer-Code) encoded in one Settings field.
  • What you need: a personal JetBrains Account API token and your JetBrains customer code.
  • Paste into the app: Settings → Provider Connections → Configure → API mode → API Token as api_key:customer_code
  • Security: the key is encrypted and stored securely server-side.

Useful links: - JetBrains Account API (Central Console) - JetBrains Account API reference - API rights reference


1. Prerequisites

  • You must be able to administer the organization in JetBrains Central Console.
  • JetBrains Account API access is only available for organizations with a verified domain (JetBrains requirement).
  • Identify your JetBrains customer code (X-Customer-Code). JetBrains documents how to obtain it via copy support information in Central Console.

2. Enable API access (organization)

  1. In JetBrains Central Console, click your organization name (bottom-left) → Org administration.
  2. Open the API section.
  3. Click Enable API (org admin required).

3. Generate a personal API token

  1. Still under Org administration → API, click Generate Token.
  2. Copy the token immediately and store it securely (JetBrains warns you cannot view it again after closing the dialog).

Notes: - JetBrains describes this as a personal API token tied to a user with eligible roles (org admin / team admin / primary billing contact). - This is not a random user token outside the organization administration flow.

4. Required access (what the token represents)

JetBrains Account API access is governed by your organization role and whether API access is enabled on the org.

Minimum: - API access enabled for the organization in Central Console. - Requests include X-Api-Key: <token> and X-Customer-Code: <customer code>.

Endpoints A-Count calls:

Capability Method Path Notes
Directory + license read GET https://account.jetbrains.com/api/v1/customer/licenses User sync and license inventory sync

There are no OAuth scope strings — access is enforced by org role and the enabled API feature.

5. License sync

  • User sync: Yes (via license assignments in the licenses API response).
  • License inventory sync: Yes (POST /api/licenses/sync/{provider_id} when provider is in API mode).

6. Install in A-Count

  1. Open Settings in A-Count.
  2. Under Provider connections, click Configure for JetBrains.
  3. Select Import mode = API.
  4. Paste into API Token using this exact format:
    • api_key:customer_code
    • (api_key|customer_code is also accepted)
  5. Click Test, then Save.

7. What A-Count does

  • Fetches JetBrains license assignments (paginated) and derives users for directory sync.
  • Syncs license inventory into A-Count for the Licenses view.
  • Prefers email as the correlation key when JetBrains exposes it on license rows.

8. Quick troubleshooting

  • 403 Forbidden: verify API access is enabled for the organization, your role is eligible, and the organization meets JetBrains prerequisites (including verified domain).
  • Token format error: use api_key:customer_code with both parts non-empty.
  • Wrong customer context: double-check X-Customer-Code (customer code) vs organization display name.
  • On‑prem license server: JetBrains Account API visibility may differ from on‑prem deployments; cloud API tokens may not reflect on‑prem allocations.

9. Rotation & security

  • Rotate API tokens on a schedule in Central Console; update A-Count and revoke the old token.
  • A-Count encrypts the stored token and does not display plaintext after save.

JumpCloud (Admin API)

This page is the operator guide for JumpCloud API access in A-Count.

Official reference: JumpCloud API 1.0.


Authentication

  • Header: x-api-key: <Admin API key>
  • Base URL: https://console.jumpcloud.com
  • A-Count storage: Provider.api_token (encrypted), API import mode

Create an Admin API key in the JumpCloud Admin Portal with read access to system users and systems.


User sync

A-Count fetches directory users from:

  • GET /api/systemusers — email, username, status, profile fields
  • GET /api/v2/users/{user_id}/systems + GET /api/systems/{system_id} — enriches last device contact for activity proxy

When JumpCloud is configured as an identity provider (API import), user sync also imports directory user groups:

Step Endpoint Purpose
Group catalog GET /api/v2/usergroups All JumpCloud user groups
Group members GET /api/v2/usergroups/{id}/membership User members per group

Data is stored in teams (ProviderTeam, team_kind=group) and idp_group_members. OU/department catalog rows are derived from department on system users when present.


Device bridge (SentinelOne EDR attribution)

When SentinelOne EDR discovered-app discovery runs, A-Count can use JumpCloud as an MDM bridge to map endpoints to corporate emails:

Step Endpoint Purpose
Device inventory GET /api/systems serialNumber, hostname, displayName, primarySystemUser
User directory (light) GET /api/systemusers Resolve primarySystemUser id/username to email
Bound users (lazy) GET /api/v2/systems/{system_id}/users Used when primarySystemUser is unset and exactly one bound user is required

Matching rules

  1. SentinelOne agent serialNumber → JumpCloud system (preferred).
  2. Fallback: agent computerName / hostname → JumpCloud hostname / displayName.
  3. User email from primarySystemUser, or from the sole bound user on the system.
  4. Systems with multiple bound users and no primary user are skipped (ambiguous).

No separate UI action is required — the bridge runs automatically during Run SentinelOne EDR / full discovered-app discovery when JumpCloud is configured.


Application discovery (Discovered Apps)

A-Count exposes two JumpCloud discovery sources on the Discovered Apps page:

JumpCloud SSO (jumpcloud_sso)

SaaS/SAML application entitlements — which users are assigned to each JumpCloud SSO app.

Step Endpoint
App catalog GET /api/applications
User directory (light) GET /api/systemusers
Users per app GET /api/v2/applications/{id}/users

API scope: applications.readonly

JumpCloud installed apps (jumpcloud_mdm)

System Insights software inventory on managed devices, attributed to users via the device bridge (primarySystemUser or single bound user).

OS Endpoint (per device)
macOS GET /api/v2/systeminsights/{system_id}/apps
Windows GET /api/v2/systeminsights/{system_id}/programs

A-Count queries per resolvable device (not the org-wide global endpoints) to avoid JumpCloud/Cloudflare timeouts on large fleets.

Requires: System Insights enabled on JumpCloud-managed devices. Transient 502/503/504 responses are retried with backoff.

Run Run JumpCloud SSO or Run JumpCloud apps from Discovered Apps, or include jumpcloud_sso / jumpcloud_mdm in POST /api/discovered-app-use/run source_types.


Minimum API rights

Capability Endpoint Required for
Directory read GET /api/systemusers User sync, SSO discovery, device bridge
Device read GET /api/systems EDR device bridge, MDM attribution
System graph read GET /api/v2/systems/{id}/users EDR/MDM user resolution when primary user unset
SSO applications read GET /api/applications JumpCloud SSO discovery (applications.readonly)
Application graph read GET /api/v2/applications/{id}/users JumpCloud SSO discovery
System Insights read GET /api/v2/systeminsights/apps, /programs JumpCloud installed-app discovery
User devices (optional) GET /api/v2/users/{id}/systems Last-activity enrichment on user sync
User groups read GET /api/v2/usergroups, GET /api/v2/usergroups/{id}/membership IdP groups page when JumpCloud is identity provider

Sync behavior (A-Count)

  • User sync: Yes — JumpCloud system users via Admin API (CSV import also supported).
  • IdP directory groups: Yes — when JumpCloud is ProviderType.IDP and import mode is api, user sync runs sync_jumpcloud_idp_groups (catalog + idp_group_members) and sync_idp_ou_department_catalog from department on accounts.
  • License sync: No dedicated JumpCloud license inventory. License model (v2): P3 catalog-only — single package ref=jumpcloud. API user sync is assign-only; first sighting materializes the package and stamps scope_id / scope_label from GET /api/organizations (displayName) when exactly one org is visible to the API key (CSV/manual commercial edits remain supported).
  • Org label: Best-effort on user sync / draft apply. Multi-org (MSP) keys leave scope_label empty. Never uses the org id as the display label.
  • Discovered app use: Yes — SSO entitlements (jumpcloud_sso), installed software via System Insights (jumpcloud_mdm), and indirect SentinelOne EDR device bridge.

Configure Looker

Quick Summary

  • Required role: Looker administrator
  • Where to go: Looker admin panel → Admin → Users → API Keys (or Admin → API3 Keys)
  • Auth model: OAuth2 client credentials (client_id + client_secret)
  • Action: create an API3 key pair for a service account user
  • Paste into A-Count: Settings → Provider Connections → Looker → Configure → API mode → fill Base URL, Client ID, Client Secret
  • Security: credentials are encrypted on save and stored server-side

1. Prerequisites

  • A Looker administrator account
  • Access to the Looker Admin panel
  • Looker instance URL (e.g. https://yourcompany.looker.com)
  • Plan/tier gates: API3 keys require Looker 4.0+; no Enterprise gate for user sync

2. Auth Model And Storage In A-Count

Item Value
Auth model OAuth2 client credentials (POST /api/4.0/login)
What A-Count stores base_url + client_id + client_secret in Provider.api_token (JSON)
Encryption ENCRYPTION_KEY (Fernet)
Token format in UI Three separate fields: Base URL, Client ID, Client Secret

3. Steps In Looker

3.1 Where To Go

  • UI path: Looker admin panel → Admin → Users → select a service account user → Edit → API Keys tab
  • Or: Admin → Platform → API → create a new API3 key

3.2 Create The Credential

  1. In Looker admin, navigate to or create a dedicated service account user (e.g. a-count-sync@yourcompany.com).
  2. Open the user's API Keys tab and click New API3 Key.
  3. Copy the Client ID and Client Secret (the secret is only shown once).

3.3 Required Permissions (role for service account)

The service account user must have a Looker role that includes:

Minimum (user sync): - see_users — list all users - access_data — basic access (needed to call /users endpoint)

Recommended (richer license deduction): - see_roles — read roles and permission sets (needed for license tier deduction via /users/{id}/roles) - see_user_login_info — read session data (needed for last_activity_at via /users/{id}/sessions)

Note: If the service account does not have see_roles, license tier deduction falls back to role name pattern matching. If it lacks see_user_login_info, activity timestamps fall back to credentials_email.logged_in_at and last_login_at.

4. Endpoints A-Count Calls

Capability Method Path Permission required
Authentication POST /api/4.0/login — (client credentials)
Directory read GET /api/4.0/users see_users
Per-user roles + permissions GET /api/4.0/users/{id}/roles?direct_association_only=false see_roles
Per-user sessions (activity) GET /api/4.0/users/{id}/sessions see_user_login_info

5. License Sync

  • User sync: Yes — all users fetched and synced
  • License inventory sync: No — Looker REST API does not expose org-level license seat counts; POST /licenses/sync returns a best-effort count by deduced tier
  • License tier deduction: Permission-based (primary) → role name pattern matching (fallback)
Deduced tier Condition
Developer (Admin) Has any permission in DEVELOPER_PERMS (e.g. administer, develop) or role name contains admin/developer
Standard (Creator) Has any permission in STANDARD_PERMS (e.g. explore, create_dashboards) or role name contains analyst/creator/finance/marketing
Viewer Has access_data but no higher-tier perms, or role name contains viewer/user/support
No licence No recognised permission or role keyword

In A-Count, deduced tiers are stored as LicensePackageType with names Looker - Developer (Admin), Looker - Standard (Creator), Looker - Viewer. Catalog ref examples: developer_admin, standard_creator, viewer. User sync assign-only when the package already exists; first sighting materializes the package (Looker has no separate inventory API).

6. Installation In A-Count

  1. Open Settings.
  2. In Provider Connections, click Configure on the Looker provider.
  3. Select Import mode = API.
  4. Fill in:
    • Base URL: https://yourcompany.looker.com (no trailing slash)
    • Client ID: the API3 key ID
    • Client Secret: the API3 key secret
  5. Click Test — a successful test authenticates and fetches 1 user.
  6. Save and run a user sync.

7. Known Limitations

  • No org-level seat count API: Looker does not expose total purchased seats via REST API. The license inventory shows counts of deduced tiers, not purchased seats.
  • Permission sets not always returned: If the service account role lacks see_roles, permission_set.permissions may be empty in /users/{id}/roles responses. License deduction falls back to role name pattern matching (less precise).
  • Session endpoint availability: /users/{id}/sessions may return 403 for some auth configurations (e.g. SAML-only instances). Activity timestamps fall back to credential login timestamps.
  • SSO/SAML users: Users who authenticate via SSO may have no credentials_email or credentials_google block. Their last_activity_at relies on sessions or last_login_at.
  • Rate limit: Looker API is rate-limited (~60 req/min on some instances). A-Count batches requests (50 users per batch, 0.5s pause between batches).

8. Quick Troubleshooting

The Token Is Rejected

  • Verify base_url has no trailing slash and uses https://.
  • Verify the client_id/client_secret belong to a user with the required role.
  • Generate a new API3 key and test again.

All Users Show License looker (Generic Fallback)

  • The service account lacks see_roles — permission sets are not returned.
  • Assign a role with see_roles to the service account and re-sync.
  • If see_roles is not available, role name pattern matching is used as a fallback.

last_activity_at Is NULL For Many Users

  • Users provisioned via SSO/SCIM who never logged in have no session or credential timestamps — this is expected.
  • Run the diagnostic query: SELECT raw->>'last_login_at', raw->'credentials_email'->>'logged_in_at', json_array_length(COALESCE(raw->'sessions', '[]'::json)) FROM provider_accounts WHERE provider_id = <id> AND last_activity_at IS NULL LIMIT 20.

9. Rotation & Security

  • Rotate the API3 key regularly via Looker admin → user → API Keys → delete old key → create new.
  • Revoke it immediately if the service account is compromised.
  • Credentials are encrypted before storage and are not shown in plain text after save.

Configure Maxon (CSV import)

Quick Summary

  • Mode: import_mode = csv (no API integration).
  • Required role: Maxon Teams Admin on my.maxon.net.
  • Where to go: Teams Dashboard → Users tab (and Licenses for seat inventory).
  • Paste into the app: Sync → select Maxon → upload CSV, or Settings → Provider Connections → Configure → CSV mode.

1. Prerequisites

  • A Maxon Teams account with admin access to the Teams Dashboard.
  • Ability to build a CSV from the Users list (Maxon does not offer a native CSV export).

2. Export From Maxon Teams Dashboard

  1. Sign in at my.maxon.net with your Teams Admin account.
  2. Open Teams from the account menu.
  3. Go to the Users tab to view team members (name, email, groups, license assignments).
  4. For license usage details, use Licenses → License Usage (shows email, license name, last online check-in).
  5. Copy the user list into a spreadsheet and save as CSV.

Normalize column headers to match the format below before upload.

3. CSV Format Expected By A-Count

Minimum required column (at least one per row):

Column Required Notes
email Yes* Primary user identifier
license_type Yes* Product name (e.g. Cinema 4D, Red Giant) for license-only rows

* Each row must have either email or license_type.

Recommended optional columns:

Column Notes
name Display name
role user or admin (default: user)
status active or inactive values (default: active)
last_activity_at From License Usage "last online check-in" if available

Extra columns are stored in raw JSON and ignored unless mapped later.

Example:

email,name,license_type,status,last_activity_at
        jane@company.com,Jane Doe,Cinema 4D,active,2026-06-15
        john@company.com,John Smith,Red Giant Complete,active,2026-06-20
        

4. Import In A-Count

  1. Open Sync.
  2. Find Maxon in the provider list.
  3. Click Sync and upload your CSV file.
  4. Review task history for row counts (valid / invalid).
  5. Run Detect anomalies if needed after import.

Alternatively: Settings → Provider Connections → Maxon → confirm Import mode = CSV.

5. Category And Billing Semantics

  • Provider category: cad_3d (overlap detection with Autodesk and similar tools).
  • Billing seat rule: assigned_until_removed — seats count while assigned until the user is removed from the directory export.

6. Known Limitations

  • No automatic API sync; each refresh requires a new CSV upload.
  • Maxon Teams Dashboard has no built-in user export; manual spreadsheet copy is required.
  • Floating vs named-user licenses may appear differently in the dashboard; use license_type to capture product names when seat assignment is unclear per user.
  • last_activity_at is only as accurate as the License Usage "last online check-in" field.

Configure Microsoft 365 (Entra ID + Intune + Defender)

Quick Summary

  • Required role: Entra ID administrator (Global Administrator recommended) to grant admin consent.
  • Portal: Microsoft Entra admin center → App registrations → your app → API permissions.
  • Official guide: Register an application.
  • Token format: tenant_id:client_id:client_secret (encrypted in A-Count).
  • Roles in A-Count Settings (per client): Identity provider, MDM discovery, EDR discovery — enable only what you need.

Minimum for user + license sync: create the app (§2), grant Graph Application permissions User.Read.All + Organization.Read.All, click Grant admin consent, paste the token in A-Count, enable Identity provider, run sync. Discovery (SSO / Intune / Defender) needs the extra permissions and roles below.


1. Prerequisites

Capability Tenant requirement
User sync / IdP Microsoft 365 / Entra ID (Free is enough for directory sync)
Sign-in activity (signInActivity) + SSO sign-in logs Entra ID P1 or P2
Last activity on Free tenants Graph usage reports fallback (Reports.Read.All; follows Graph 302 CSV download)
Intune MDM discovery Intune license
Defender EDR discovery Microsoft Defender for Endpoint plan

User sync probes signInActivity once per run: P1/P2 tenants use it; Free tenants skip it and compute last_activity_at from usage reports. Directory sync does not require P1.


2. Create the Entra app registration

Follow Microsoft’s guide: Register an application with the Microsoft identity platform.

  1. Open the Microsoft Entra admin center (sign in as Global Admin or Application Administrator).
  2. Go to Entra ID → App registrations (direct link: App registrations).
  3. Click New registration.
  4. Name: e.g. A-Count.
  5. Supported account types: Accounts in this organizational directory only (single tenant).
  6. Leave Redirect URI empty (A-Count uses client credentials, not interactive login).
  7. Click Register.

Copy credentials for A-Count

On the app Overview page:

Entra field A-Count token part
Directory (tenant) ID tenant_id
Application (client) ID client_id

Then create a secret:

  1. Certificates & secrets → New client secret.
  2. Add a description and expiry → Add.
  3. Copy the Value immediately (shown once) → that is client_secret.

Paste into A-Count as: tenant_id:client_id:client_secret (three parts, colon-separated, no spaces).


3. API Permissions (Application type)

Still in the app: API permissions → Add a permission.

Important:

  • Use Application permissions (not Delegated).
  • After adding all permissions, click Grant admin consent for \<tenant>. Without this step, sync and Test fail with 403.
  • Permission reference: Microsoft Graph permissions.

Microsoft Graph

Add a permission → Microsoft Graph → Application permissions:

Permission Used for
User.Read.All User directory sync (Source of Truth) — required for sync
Organization.Read.All License inventory (subscribedSkus) — required for license sync
Directory.Read.All App role assignments, OAuth grants, device bridge, role/admin detection
Application.Read.All Enterprise applications catalog; optional user sync of service principals (GET /v1.0/servicePrincipals with Enterprise-apps filter + max cap) as employee_type=service_account (probed once per sync; 403 skips without failing user sync)
AuditLog.Read.All Sign-in logs (SSO usage discovery + last sign-in)
GroupMember.Read.All Group-based SSO assignments; team scope in user sync (GET /users/{id}/memberOf); OIDC auto-add group membership (checkMemberGroups)
Group.Read.All Resolve authorized group by display name for OIDC auto-add (also covered by Directory.Read.All)
DeviceManagementManagedDevices.Read.All Intune MDM discovery
Reports.Read.All M365 activity reports fallback when signInActivity is unavailable (last_activity_at)

WindowsDefenderATP API (EDR only)

Add a permission → APIs my organization uses → search WindowsDefenderATP → Application permissions:

Permission Used for
Machine.Read.All List Defender endpoints
Software.Read.All Installed software per endpoint

Token audience: client-credentials scope must be https://api.securitycenter.microsoft.com/.default (legacy WindowsDefenderATP resource). Calling https://api.security.microsoft.com/api/machines with a token issued for api.security.microsoft.com often returns 403 even when these permissions are granted.

Do not add AppRoleAssignment.ReadWrite.All for read-only discovery. Reading assignments uses Directory.Read.All + Application.Read.All.


4. Installation In A-Count

  1. Settings → Provider Connections → Microsoft → Configure.
  2. Import mode = API.
  3. API Token: tenant_id:client_id:client_secret.
  4. Optional api_config JSON:
{
          "email_normalization_domain": "example.com",
          "discovery": {
            "sso_signin_lookback_days": 90,
            "defender_enabled": true,
            "defender_api_base": "https://api.security.microsoft.com"
          }
        }
        
  1. Click Test, then Save.

Disable Azure AD group fetch during user sync with env MICROSOFT_FETCH_GROUPS=0 (legacy alias OFFICE365_FETCH_GROUPS). Group memberOf enrichment uses bounded concurrency (default MICROSOFT_MEMBEROF_CONCURRENCY=5, same for all tenants).


5. Configure Roles (per client)

In Settings → Provider Connections → Microsoft toggles:

Toggle Enables
Identity provider Authoritative user sync + SSO discovery (idp)
MDM discovery role Intune installed-apps discovery (mdm)
EDR discovery role Defender endpoint software discovery (edr)

Other providers (Google, JumpCloud, SentinelOne) can remain enabled alongside Microsoft during transition.


6. What A-Count Does

User sync (IdP / Source of Truth)

  • GET /v1.0/users with optional signInActivity, including department when present on the directory object
  • Best sign-in timestamp: max of lastSuccessfulSignInDateTime, lastSignInDateTime, lastNonInteractiveSignInDateTime
  • If signInActivity is unavailable, computes last_activity_at from Graph Reports (period D180 by default) and stores the source key in raw.last_activity_source
  • Per-user licenses via GET /v1.0/users/{id}/licenseDetails
  • License inventory via GET /v1.0/subscribedSkus
  • Team scope enrichment (default on): GET /v1.0/users/{id}/memberOf stores Azure AD group display names in raw.groups[] as {id, name}. department is also used as a team label when set. Directory roles (e.g. Global Administrator) are excluded.

SSO discovery (MicrosoftSsoSource)

  • Usage: GET /auditLogs/signIns (last N days, default 90)
  • Entitlement: GET /users/{id}/appRoleAssignments

MDM discovery (MicrosoftIntuneMdmSource)

  • GET /deviceManagement/managedDevices
  • GET /deviceManagement/detectedApps + per-app device linkage

EDR discovery (MicrosoftDefenderEdrSource)

  • GET https://api.security.microsoft.com/api/machines
  • GET /api/machines/{id}/software
  • Device → user attribution via Intune index, then OS username fallback

Run discovery from Discovered Apps (POST /api/discovered-app-use/run) or per-category buttons.


7. Rollout checklist

  1. Create the Entra app (§2); add Graph (+ WindowsDefenderATP if EDR); Grant admin consent.
  2. Configure Microsoft in A-Count (§4); Test then Save.
  3. Enable Microsoft as Identity provider (and MDM / EDR if needed).
  4. Run full Microsoft Sync (licenses then users).
  5. Run discovery: IdP → MDM → EDR (or full run).
  6. Authorize discovered apps to move them in_scope.
  7. Optionally disable JumpCloud/SentinelOne discovery roles if Microsoft covers the tenant.

9. Graph endpoints reference

  • POST /oauth2/v2.0/token (Graph scope https://graph.microsoft.com/.default; Defender scope https://api.securitycenter.microsoft.com/.default)
  • GET /v1.0/users, /subscribedSkus, /auditLogs/signIns
  • GET /v1.0/users/{id}/licenseDetails, /users/{id}/memberOf
  • GET /v1.0/servicePrincipals (optional; requires Application.Read.All; probed before fetch)
  • GET /v1.0/reports/*UserDetail(period='D180') (fallback activity mode)
  • GET /deviceManagement/managedDevices, /deviceManagement/detectedApps
  • GET https://api.security.microsoft.com/api/machines, /api/machines/{id}/software

Configure Miro

Quick Summary

  • Required plan: Miro Enterprise (organization-level admin APIs are restricted on lower plans).
  • Required role: Miro Company Admin.
  • Where to go: Go to Miro Your Apps.
  • What you need: an OAuth access token generated from your Miro app installation.
  • Security: the token is encrypted and stored securely server-side.

Useful links: - Go to Miro Your Apps - Go to Add permission scopes to your app


1. Prerequisites

  • Plan check: make sure your organization is on Enterprise. On Starter/Business plans, A-Count may not be able to retrieve the full user directory via organization APIs.
  • Role: you must be a Company Admin (token creation and org-wide access requirements).

2. Create The App And Token

  1. Go to Miro Your Apps.
  2. Click Create new app.
  3. Name the app (example: A-Count-Sync) and confirm the correct team/organization context.
  4. In the left menu, open App permissions.

3. Required Access (Scopes)

Enable the following scopes (org-wide directory access is typically Enterprise-gated):

  • organizations:read: required to resolve organization context and list organization members.
  • identity:read: helps retrieve user identity fields such as email/name when exposed by Miro for your tenant configuration.
  • team:read: helps enumerate team membership context when needed.

Important: - Click Save changes before continuing. - If you change scopes after the first install, you must Reinstall the app so the token permissions refresh.

4. Install And Generate Token

  1. Open Install app and get OAuth token.
  2. Click Install app (or Reinstall if the app was already installed).
  3. Approve the permissions in Miro.
  4. Copy the Access Token shown after installation.

5. Install In A-Count

  1. Open Settings in A-Count.
  2. Under Provider connections, click Configure for Miro.
  3. Select API mode.
  4. Paste the token into API token.
  5. Click Test, then Save.

6. What A-Count Does

  • A-Count uses your Miro token to call Miro APIs to fetch users for synchronization.
  • Depending on your tenant configuration, A-Count may try SCIM first, then fall back to organization members APIs when SCIM is not available.
  • License model (v2): inventory groups users by Miro license slug; catalog ref = lowercase license slug (e.g. full). User sync is assign-only against that catalog (P3 / catalog-only unless a stable org id is later used as P2 contract_ref).

7. Troubleshooting (Miro-specific)

  • 403 Forbidden: the most common signal that you are not on Enterprise, you are not Company Admin, or your app/token is missing required org scopes (notably organizations:read).
  • Empty user list: verify you enabled organizations:read (and related scopes you need) before installing the app. If you added scopes later, click Reinstall to refresh the token.
  • Token works but still fails org sync: confirm the app is installed under the correct organization context and that the token was generated after the reinstall.

8. Advanced (Optional)

If your organization uses SCIM provisioning, your deployment may also support a dedicated SCIM token via server configuration (MIRO_SCIM_TOKEN). This path is optional and depends on how your Miro tenant is set up.

Configure Mixpanel

Quick Summary

  • Required role: Mixpanel organization Owner or Admin (for the Service Account)
  • Where to go: Organization Settings → Service Accounts
  • Auth model: HTTP Basic (service_account_username:service_account_secret)
  • Optional: Enterprise SCIM token via server env MIXPANEL_SCIM_TOKEN
  • Paste into the app: Settings → Provider Connections → Configure → API mode → API Token
  • Security: credentials are encrypted on save and stored server-side
  • API rights reference: api-rights-reference.md

1. Prerequisites

  • A Mixpanel organization with permission to create Service Accounts
  • For SCIM (optional): Enterprise plan with SSO/SCIM enabled
  • Do not use a Project Token or Project Secret for member sync — use a Service Account only

2. Auth Model And Storage In A-Count

Item Value
Auth model HTTP Basic (Service Account)
What A-Count stores Provider.api_token as username:secret
Optional SCIM MIXPANEL_SCIM_TOKEN in backend .env (Bearer), not in the UI
Encryption ENCRYPTION_KEY / MASTER_KEY

3. Steps In Mixpanel

3.1 Create a Service Account

  1. Open Mixpanel → Organization Settings → Service Accounts.
  2. Create a service account with a descriptive name (e.g. A-Count sync).
  3. Copy the username and secret (secret is shown once).

3.2 Optional: SCIM token (Enterprise)

  1. Organization Settings → Security → SCIM.
  2. Generate a SCIM provisioning token.
  3. Set on the API server: MIXPANEL_SCIM_TOKEN=<token> and restart the backend.

When SCIM is configured, A-Count syncs human users from SCIM and adds service accounts from /api/app/me.

3.3 Token format in A-Count

Paste a single line into API Token:

<service_account_username>:<service_account_secret>
        

If the secret contains : characters, only the first colon separates username from secret.

4. Endpoints A-Count Calls

Capability Method Path Notes
Member directory (primary) GET https://mixpanel.com/api/app/me Reads results.workspaces (list or id-keyed dict) → unified_member_list only
SCIM users (optional) GET https://mixpanel.com/api/app/scim/v2/Users Paginated; requires MIXPANEL_SCIM_TOKEN

results.workspaces shape varies by tenant (e.g. SuperPlay returns a dict keyed by workspace id). A-Count normalizes both shapes via _iter_workspace_dicts before reading unified_member_list.

5. Data Mapped In A-Count

Mixpanel field (unified_member_list row) A-Count field Notes
id provider_user_key Stable member id (string)
email email Empty email allowed; username falls back to mixpanel_{id}
name name Via MixpanelMember.display_name
invited status true → invited; service accounts → active
entity_type employee_type user or service_account (lowercased); not mapped to role
membership_type raw.mixpanel.membership_type Preserved in raw only
team_name job_title, raw.mixpanel.team_name extract_job_title reads raw.mixpanel.team_name (or nested member.team_name)
org_name raw.mixpanel.org_name Org context for reporting / anomalies
Workspace id / name raw.mixpanel.workspace_id, raw.mixpanel.workspace_name Passed from parent workspace, not only inside member
Full member row raw.mixpanel.member Original /me row for audit
SCIM user raw.mixpanel.scim When row comes from SCIM; entity_type forced to user

Unified role: always user. Do not map results.organizations[].role, results.projects[].role, or permissions[] — those describe the authenticated Service Account or project defaults, not per-member rows in unified_member_list.

last_activity_at: always None — /me and SCIM member rows in production exports do not expose last_activity / last_login.

6. License Sync

  • User sync: Yes (API mode)
  • License inventory sync: No — billing uses infrastructure seat rule; combined sync skips license inventory
  • License model (v2): P3 catalog-only — single package ref=mixpanel. API user sync is assign-only; first sighting materializes the package (no vendor SKU inventory). organization_id may later be a P2 contract_ref.
  • Default license on user rows: mixpanel (license_ref=mixpanel)

7. Installation In A-Count

  1. Open Settings.
  2. Find Mixpanel → Configure.
  3. Set Import mode = API.
  4. Paste username:secret → Test (validates /me; also probes SCIM if env is set).
  5. Run Sync (Users) from the Sync page or scheduler.

7. Limitations

  • last_activity_at: not available from /me or standard SCIM
  • Large /me payloads: sync uses unified_member_list only (not member_list); read timeout up to 120s
  • Roles: no per-member app role in unified_member_list; A-Count stores entity_type in raw
  • Engage / analytics APIs: different credentials; not used for directory sync

8. Troubleshooting

Symptom Likely cause
Test OK but Sync page returns HTTP 500 Fixed in backend: POST /api/providers/{id}/sync must pass current_user into user sync (was TypeError before fix)
Test returns HTML / invalid auth Wrong token type (project secret) or bad username:secret
403 on /me Service account lacks org Owner/Admin visibility
SCIM 403 Enterprise/SSO/SCIM not enabled
Duplicate users across workspaces Handled by global dedup on member id
Empty user list after sync but /me export has users /me may return results.workspaces as a dict (not a list); backend must iterate dict values and read unified_member_list per workspace
Empty user list No workspaces visible to the service account, or unified_member_list empty (users only in member_list)

9. Field mapping: Mixpanel API → A-Count (inventory)

Validated against a real GET /api/app/me export (SuperPlay tenant, 2026-06-03): ~476 unique member ids in unified_member_list, workspaces as a dict keyed by workspace id.

Short answers

Question Answer today
Roles imported into ProviderAccount.role? No — always hardcoded "user" in the converter. Mixpanel entity_type / membership_type / org role are not mapped to role.
last_activity_at imported? No — always NULL. Not present on member rows in /me or standard SCIM in this integration.
Dates at all? Only indirectly: member invited → status (invited / active); SCIM active → inactive / active. No login/last-seen datetime.

9.1 Endpoints used vs ignored

Mixpanel surface Used by A-Count? Code
GET /api/app/me Yes (member directory) backend/data_providers/mixpanel.py — get_me_raw, get_users
GET /api/app/scim/v2/Users Optional (MIXPANEL_SCIM_TOKEN) backend/data_providers/mixpanel.py — _get_users_via_scim
results.projects No Not read (project metadata + SA permissions, not per-user directory)
results.organizations No Not read (org-level role / permissions for org context, not per member)
member_list (per workspace) No Ignored by design (volume); only unified_member_list
Engage / Query / Export APIs No Different credentials; out of scope for directory sync

9.2 GET /api/app/me — unified_member_list[] (per workspace)

Mixpanel field (member row) Example A-Count today DB / raw Gap & where to change
id 4083187 Yes ProviderAccount.provider_user_key — schemas/mixpanel.py from_me_record; user_service.py MixpanelUserConverter
email Itayk@superplay.co Yes email, username (if email present) — same
name Itay Karlin Yes name — same
invited false Partial status = invited if true, else active Does not store invitation date (not in API row)
entity_type user / service_account Partial raw.mixpanel.entity_type + full row in raw.mixpanel.member Not mapped to ProviderAccount.role — set in MixpanelUserConverter.to_unified (role="user") or map entity_type → role / employee_type
membership_type team / individual Partial raw.mixpanel.membership_type + raw.mixpanel.member Not top-level column — add field on MixpanelMember in schemas/mixpanel.py from_me_record, then map in MixpanelUserConverter (e.g. employee_type)
team_name Disney Solitaire - Analysts Raw only raw.mixpanel.member.team_name Not promoted to structured raw.mixpanel keys — extend raw_payload in schemas/mixpanel.py or map to job_title via lib/provider_job_title.py (no mixpanel rules today)
org_name "" Raw only raw.mixpanel.member.org_name Same as team_name
last_activity / last_login / last_seen — No last_activity_at = NULL Not in API payload for this endpoint; would need another Mixpanel API or manual CSV — set in MixpanelUserConverter only if a source exists
Mixpanel org/project admin role per user — No role = "user" always Org role exists on results.organizations[id].role for the authenticated principal, not per unified_member_list row — would need SCIM groups, custom roles API, or member_list + extra endpoints

9.3 GET /api/app/me — other results.* keys (not per member)

Location Contents Imported? If needed, change
results.user_id, user_email, user_name Service Account identity No (sync is member directory, not SA profile) data_providers/mixpanel.py — only if you want SA metadata on provider config
results.date_joined_iso SA join date No Same
results.organizations[id].role e.g. admin for org No Not per-member; do not map to all users
results.organizations[id].permissions[] Permission strings No Org-level only
results.projects[id].role, .permissions Project access for SA No Not a user directory
results.workspaces[id].name, .project_id, .created_iso, … Workspace metadata No (except iterating workspaces for members) Optional: attach workspace_id / workspace_name to each member in from_me_record if product needs scope

9.4 member_list[] (not used today)

Field In export? A-Count To use member_list
id, name, email, invited Yes Ignored data_providers/mixpanel.py — _iter_unified_members or new iterator + fallback when unified_member_list empty; warning: large payloads

9.5 SCIM GET /api/app/scim/v2/Users (optional)

SCIM field A-Count today Gap & where to change
id Yes → provider_user_key schemas/mixpanel.py from_scim_record
userName / emails[] Yes → email —
displayName / name Yes → name —
active Yes → status active/inactive —
last_activity / custom Mixpanel extensions No Only if present in your tenant SCIM schema — extend from_scim_record + converter
Groups / roles No Parse groups / roles in from_scim_record → ProviderAccount.role

9.6 ProviderAccount columns after sync

Column Mixpanel source Today Code
provider_user_key id Yes MixpanelUserConverter
email, username, name email, name Yes same
status invited / SCIM active Partial schemas/mixpanel.py status derivation
role — Fixed "user" backend/services/user_service.py MixpanelUserConverter.to_unified line ~1026
employee_type — NULL Map from entity_type or membership_type in converter
job_title — NULL users_sync.py calls extract_job_title — add mixpanel rules in backend/lib/provider_job_title.py (e.g. team_name from raw)
last_activity_at Not in API NULL MixpanelUserConverter — only if you add a new data source
license_type_id N/A (infra billing) Assign-only mixpanel backend/routers/users/users_sync.py ~1092 assign_license_name_only
raw Subset + member blob Yes schemas/mixpanel.py raw_payload
  1. P0 — Already done: parse workspaces as dict or list (data_providers/mixpanel.py _iter_workspace_dicts).
  2. P1 — Role semantics: map entity_type → employee_type or role (service_account vs user); optionally membership_type in schemas/mixpanel.py + MixpanelUserConverter.
  3. P1 — Context in UI: promote team_name, org_name, workspace name/id into raw.mixpanel (not only nested member) in raw_payload.
  4. P2 — member_list fallback: only if unified_member_list empty and product accepts payload size — mixpanel.py get_users.
  5. P3 — last_activity_at: requires Mixpanel API that exposes per-user last activity (not in current /me or SCIM fixtures) — new provider function + converter field; document impossibility until API confirmed.

9.8 Code map (single reference)

Layer File Responsibility
HTTP + merge SCIM//me backend/data_providers/mixpanel.py get_users, _iter_unified_members, _iter_workspace_dicts
Normalized row backend/schemas/mixpanel.py MixpanelMember, from_me_record, from_scim_record, raw_payload
DB account shape backend/services/user_service.py MixpanelUserConverter
Upsert + license label backend/routers/users/users_sync.py sync loop, assign_license_name_only for mixpanel
Token test backend/data_providers/token_validator.py probe_me_access, probe_scim_access
Wiring backend/routers/users/constants.py "mixpanel": mixpanel_get_users
Tests backend/tests/providers/test_mixpanel_provider.py, backend/tests/fixtures/mixpanel/ Parsing / dedup

Configure Monday

Quick Summary

  • Required role: Monday Admin (recommended for full directory visibility) or a user allowed to generate API tokens.
  • Where to go: Monday Developer Center or Administration → Connections → Personal API token (depending on your user type).
  • What you need: a Personal API Token.
  • Extra field in A-Count: None (optional Enterprise SCIM is server env only).
  • Security: the token is encrypted when saved server-side.

Useful links: - Go to Monday API authentication - Users / API-Version 2026-07 - SCIM Provisioning API (Enterprise)


1. Prerequisites

  • You must have an active Monday account (the token is tied to the user who generates it).
  • The token user must be able to see the user directory so A-Count can list members (admin tokens are the most reliable).

2. Create The Token

Monday supports two official paths to access your personal API token (depending on your user type):

Option A: Developer Center (all users with API access)

  1. In your monday.com account, click your profile picture.
  2. Select Developers (opens the Developer Center).
  3. Click API token → Show.
  4. Copy your personal token.

Option B: Administration tab (account admins only)

  1. In your monday.com account, click your profile picture.
  2. Select Administration → Connections → Personal API token.
  3. Copy your personal token.

3. Required Access

A-Count uses Monday’s GraphQL API (https://api.monday.com/v2/) with API-Version: 2026-07 (override via MONDAY_API_VERSION).

Your token must be able to read: - Users (users): active, pending, and inactive (paginated; max 1000 per page). - Identity / role: status, kind / user_config, email, name, title, last_activity. - Account metadata: account.tier, account.products { kind, tier }, active_members_count. - Seat inventory signal: apps_monetization_info.seats_count (account seat size).

License / seat notes

  • Monday does not expose a queryable per-user product assignment field on the public GraphQL API (account_products appears in introspection but queries fail).
  • A-Count therefore binds:
    • Paid / member seats from account.tier (+ all account.products as apps).
    • Guest / viewer seat classes from user kind (or legacy is_guest / is_view_only).
  • Org seat capacity uses apps_monetization_info.seats_count and active_members_count when available.

4. Install In A-Count

  1. Open Settings in A-Count.
  2. Under Provider connections, click Configure for Monday.
  3. Select API mode.
  4. Paste the token into API token.
  5. Click Test.
  6. Save.

5. Optional Enterprise SCIM enrichment

SCIM does not replace GraphQL for license/apps. When configured, A-Count enriches GraphQL users with SCIM userType / active flags.

  1. Admin → Security → SCIM → generate bearer token.
  2. Set server env:
    • MONDAY_SCIM_TOKEN=<scim bearer>
    • MONDAY_SCIM_DOMAIN=<account-slug> (optional if GraphQL returns account.slug, e.g. superplay for superplay.monday.com)

The GraphQL personal API token is not valid for SCIM (SCIM returns 403).

6. What A-Count Does

  • A-Count uses your Monday.com API token to fetch users (and optionally enrich via SCIM).
  • License model (v2): inventory groups by account tier; catalog ref = slugified tier (e.g. pro). User sync stamps license_ref and assign-only links to the existing catalog row.
  • Extraction: GraphQL user pagination (status: ACTIVE|PENDING|INACTIVE) + account inventory query.
  • Matching: email as the primary key across tools (when present).
  • Security: after a successful test, the GraphQL token is encrypted in the database.

7. Quick Troubleshooting

  • 401 Unauthorized: the token was copied incorrectly or was regenerated in Monday.
  • Missing users: the token inherits the visibility of the user who created it. Prefer an Admin token.
  • GraphQL errors: check IP allowlists; confirm MONDAY_API_VERSION is 2026-01 or later for products.tier.
  • SCIM 403: wrong token type or non-Enterprise plan; use the SCIM bearer from Admin → Security → SCIM.
  • Empty apps: ensure the API version supports AccountProduct.tier and that the account has multi-product products returned.

Configure NetSuite

Quick summary

  • Auth: OAuth 1.0 Token-Based Authentication (TBA), HMAC-SHA256.
  • API token format (single field): account_id:consumer_key:consumer_secret:token_id:token_secret
  • Host: production only — {account_id}.suitetalk.api.netsuite.com
  • User sync: SuiteQL on employee where giveaccess = 'T' (NetSuite login / licence seat only).
  • Paste token: Settings → Provider Connections → NetSuite → Configure → API → five TBA fields (Account ID, Consumer Key, Consumer Secret, Token ID, Token Secret).

Official reference: NetSuite TBA


1. Prerequisites in NetSuite

  1. Setup → Company → Enable Features → SuiteCloud
    • REST Web Services
    • Token-Based Authentication
    • SuiteQL
  2. Setup → Integration → Manage Integrations — create integration; note Consumer Key / Consumer Secret.
  3. Setup → Users/Roles → Access Tokens — create token for a role with:
    • SuiteQL permission
    • Employee list/record read (View minimum)
  4. Copy Account ID (Setup → Company → Company Information).

2. Configure A-Count

  1. Set import mode to API.
  2. Open NetSuite → Configure and enter the five TBA fields separately:
    • Account ID
    • Consumer Key / Client ID
    • Consumer Secret / Client Secret
    • Token ID
    • Token Secret
  3. Click Test connection — runs a lightweight SuiteQL probe on employee with giveaccess = 'T'.
  4. Save, then run user sync.

The UI joins these five values into one encrypted composite token (account_id:consumer_key:consumer_secret:token_id:token_secret) before sending them to the API.


3. Data mapped in A-Count

SuiteQL field A-Count field Notes
id provider_user_key Internal NetSuite id
email email Required; rows without email are skipped
firstname + lastname (fallback entityid) name
isinactive (T / F) status inactive when T
role (Administrator / User) role admin / user; admin = EmployeeRoles with selectedrole = 3 and centertype = 'BASIC'
department raw.employee.department Via BUILTIN.DF(e.department)
lastmodifieddate last_activity_at Activity proxy for NetSuite
hiredate, lastmodifieddate, datecreated raw.employee.* Preserved for future anomaly rules
giveaccess (filter) Only T rows are synced
Full row raw source: netsuite_suiteql

Employees with giveaccess = 'F' are not imported (no NetSuite login seat).

Licenses: NetSuite has no SKU/plan inventory in A-Count. POST /api/licenses/sync/{id} returns licenses_upserted: 0. License model (v2): P3/mono catalog-only — single package ref=netsuite. API user sync is assign-only; first sighting materializes the package with scope_id=account_id and best-effort scope_label from SuiteQL companyinformation.companyname (never the account id as label).

Sandbox (-sb1, -sb2 hosts): not supported in v1; production account id only.


4. Common errors

Symptom Likely cause
401 on test/sync Wrong consumer/token secrets or TBA not enabled
403 Role missing SuiteQL or Employee permissions
400 SuiteQL SuiteQL feature not enabled on the account
Empty sync Employees exist but have no email in NetSuite

Configure OpenAI (ChatGPT)

Quick summary

  • Default: import_mode = csv (export members from ChatGPT admin).
  • API mode: ChatGPT Enterprise only — Compliance API key + workspace id.
  • Not supported for ChatGPT seats: Platform Admin API keys (sk-admin-...), Codex Access tokens, WorkOS Directory Sync credentials.
  • Leave existing Directory Sync (SCIM / Google Workspace) alone — that provisions ChatGPT from your IdP; A-Count reads members via Compliance API.

Official docs:


1. When to use CSV vs API

Plan Recommended mode Why
Business CSV No Compliance user directory API / no SCIM read token.
Enterprise API (if Compliance key works) Lists ChatGPT workspace members via Compliance API.

If API token test fails, stay on CSV — A-Count does not auto-fallback during sync.


2. Enterprise — Compliance API key + workspace id

There is no UI label “Compliance-scoped API key”. You create a normal Platform secret key in the same org as the ChatGPT workspace, then OpenAI Support grants Compliance scopes on that key.

2.1 Workspace ID (you already have this)

ChatGPT admin → General / chatgpt.com/admin/settings → Workspace details → Workspace ID.

Also note Organization ID on the same page (needed for the Support email).

2.2 Create a Platform secret key

  1. Open platform.openai.com/settings/organization/general.
  2. Confirm the Organization ID matches ChatGPT admin (wrong org = wrong key).
  3. Go to API keys → Create new secret key.
  4. Prefer Service account, name it clearly (e.g. a-count-compliance), project Default, permissions All (OpenAI will replace these with Compliance scopes later).
  5. Copy the key once (shown only at creation).

Do not use ChatGPT sidebar Access tokens (Codex) or Platform Admin keys (sk-admin-...).

2.3 Request Compliance scopes from OpenAI

Email support@openai.com (workspace Owner) with something like:

Subject: Enable Compliance API scopes for ChatGPT Enterprise key

        Please grant Compliance API scopes on the following API key for our ChatGPT Enterprise workspace.

        - Organization ID: <from ChatGPT admin / Platform org settings>
        - Workspace ID: 403b3d43-c36c-430a-9c6c-c4a3f83e3c98
        - API key name: <name you chose>
        - Created by: <display name of creator>
        - Last 4 characters of the API key: <xxxx>
        - Requested scopes: READ (user directory / compliance read for A-Count sync)

        This key is for Compliance API access only.
        

After OpenAI confirms, other scopes on that key are typically revoked and Compliance scopes are enabled. Then paste the key into A-Count.

2.4 Configure A-Count

  1. Set import_mode = API.
  2. Paste the key as API Token.
  3. Set api_config.openai_workspace_id = 403b3d43-c36c-430a-9c6c-c4a3f83e3c98.
  4. Test token → run user sync.

A-Count calls:

  • GET https://api.chatgpt.com/v1/compliance/workspaces/{workspace_id}/users (paginated with after / last_id)

3. Data mapped in A-Count

Compliance field A-Count field Notes
id provider_user_key Stable user id
email email Required; rows without email are skipped
name / full_name name
role role Mapped to admin or user
created_at / created (not mapped to last_activity) Kept in raw only — no reliable last-activity API yet
Full record raw source: openai_compliance_api

Licenses: no public seat inventory API — each synced member is assigned license ChatGPT.


4. Wrong credentials (common errors)

Credential Result
Platform / ChatGPT admin key (sk-admin-...) Allowed to attempt Compliance; may still 401/403 if scopes are wrong
Codex Access token Wrong product — local Codex automation
WorkOS Directory Sync IdP → ChatGPT only; no pasteable read token for A-Count
Missing openai_workspace_id Sync / test fails with workspace_id required
Business plan Compliance Test/sync 403 → use CSV

Configure Parsec

Quick Summary

Auth Teams API key (Authorization: Bearer)
Required Enterprise Public API key with View Team Members + Team ID
Credentials Encrypted Teams API key (api_token) + api_config.parsec_team_id
User sync Yes (GET /v1/teams/{teamID}/members, active + inactive)
License sync Yes (roster-derived assigned seats from active members)
Dual-mode Default CSV; switch to API in Settings
API rights reference api-rights-reference.md

Official docs:


1. Prerequisites

Prerequisite Why
Parsec for Teams Enterprise Teams API is Enterprise-only (pricing). Individuals / standard Teams plans do not include it.
Super Admin Only Super Admins can create Public API keys
Public API key with View Team Members Required to list members
Team ID Path parameter on member endpoints; also embedded in tapi_<teamId>.… keys

If Keys only shows a Computer Key and there is no Create Key / Public API Key option, the team is almost certainly on Teams (not Enterprise). Keep CSV mode in A-Count, or upgrade to Enterprise with Parsec.


2. Auth Model And Storage In A-Count

Item Value
Auth model Bearer Teams API key
What A-Count stores Encrypted Provider.api_token = Teams API key; api_config.parsec_team_id
Encryption ENCRYPTION_KEY (Fernet) for the API key
Team ID in UI Separate field (also accepts URL / tapi_ paste)

3. Steps In Parsec

  1. Confirm the team is on Enterprise (pricing — “Access Parsec's Teams API…” is listed under Enterprise only).
  2. Sign in as a Super Admin.
  3. In the Teams admin portal → Keys → create a Public API Key (not a Computer Key / tmkey_…).
  4. Grant at least View Team Members.
  5. Copy the Team ID (dashboard sidebar / URL, or the tapi_<teamId>.… key prefix).
  6. In A-Count Settings → Parsec → API: paste Team ID + API key → Test → Save.
  7. Run user sync (license sync is included in combined provider sync).

Wrong key type

A Computer Key (tmkey_… on Keys) is for team machines. A-Count needs a Public API Key (tapi_…).

SCIM note

Parsec SCIM is IdP → Parsec push provisioning (Okta/Azure). A-Count does not pull from the SCIM tenant URL. Members provisioned via SCIM still appear on the Teams API members list (is_scim).


4. Endpoints A-Count Calls

Capability Method Path Notes
Members GET /v1/teams/{teamID}/members offset/limit pagination; requests status=active and status=inactive

Base host: https://api.parsec.app.

Mapped fields:

  • email, name, is_admin → identity / role
  • is_active → account status (inactive → deleted in A-Count)
  • last_connected_at → last_activity_at
  • Active members → assigned Parsec Teams seats

5. License Sync

  • Inventory is a single Parsec Teams package with assigned count = active members.
  • Purchased spare seats are not exposed on the members API — keep CSV for entitlement totals if needed.
  • Inactive / SCIM-deprovisioned members no longer consume a seat (per Parsec docs) and are excluded from the assigned count.

6. Troubleshooting

Symptom Likely cause Fix
No Create Key / only Computer Key Team not on Enterprise (Teams API Enterprise-only) Upgrade to Enterprise, or stay on CSV
401/403 Wrong key or missing View Team Members Recreate Public API Key with the permission
404 Wrong team ID Confirm team id from dashboard URL or tapi_<teamId>.…
Rate limit 429 API throttle Sync retries with backoff; re-run later if needed
Empty seats No active members Confirm is_active users exist on Members & Invites

Configure SentinelOne

Quick Summary

  • Required role: SentinelOne administrator or service user with user-read and account-read access.
  • Where to go: SentinelOne Management Console -> Settings -> Users or Service Users.
  • Auth model: API token sent as Authorization: ApiToken <token> for user, site, and account reads.
  • Action: generate an API token and copy the management console URL.
  • Paste into the app: Settings -> Provider Connections -> Configure -> API mode -> SentinelOne URL + API Token.
  • Security: the API token is encrypted on save and stored server-side; never paste it in chat, tickets, or docs.
  • API rights reference: api-rights-reference.md

1. Prerequisites

  • A SentinelOne Management Console admin account, preferably a dedicated service user.
  • The tenant management URL, for example https://euce1-108.sentinelone.net.
  • An API token whose scope can read console users and accounts.

2. Auth Model And Storage In A-Count

Item Value
Auth model API token header for user, site, and account reads
What A-Count stores Provider.api_token stores the encrypted token; Provider.api_config["sentinelone_base_url"] stores the non-secret management URL
Encryption ENCRYPTION_KEY for Provider.api_token
Token format in UI Plain SentinelOne API token only, without the ApiToken prefix
URL format in UI Full HTTPS management console URL, for example https://euce1-108.sentinelone.net

3. Steps In SentinelOne

3.1 Where To Go

  • Direct URL: open your tenant management console, for example https://euce1-108.sentinelone.net.
  • UI path: Settings -> Users -> select an admin/service user -> generate or view API token options.
  • For service users, use Settings -> Users -> Service Users when available in your tenant.

3.2 Create The Credential

  1. Create or select a dedicated service user.
  2. Give the service user the minimum scope that can view users.
  3. Generate an API token.
  4. Copy the token immediately; SentinelOne may show it only once.
  5. Copy the tenant management URL from the browser address bar.

3.3 Required Scopes / Permissions

Minimum (user sync): - User view/read permission: required for GET /web/api/v2.1/users. - User read permission is authoritative for validation; A-Count does not call login-by-token.

Minimum (license inventory sync): - Site view/read permission: required for the preferred inventory path GET /web/api/v2.1/sites. - Account view/read permission: required for the fallback inventory path GET /web/api/v2.1/accounts. - A-Count stores one license inventory row per SentinelOne SKU/plan, for example Complete. Site/account details are aggregated under LicensePackageType.raw["provider_meta"]["sentinelone"]["items"]. - If /sites is not available to the token, A-Count falls back to the same SKU/plan aggregation from /accounts. - The site/account expiration field is treated as the license expiration/renewal date and is exposed through LicensePackageType.raw["manual_renew_date"].

Recommended: - Scope the token to the SentinelOne account/site that should be audited by A-Count. - Use a service user rather than a human admin token so rotation and ownership are clear.

License sync: - Supported through the SentinelOne accounts endpoint. This is account/agent inventory, not per-console-user license assignment.

  • Not applicable for API tokens. If permissions change, generate a new token and replace it in A-Count.

4. Endpoints A-Count Calls

Capability Method Path / operation Permission required
Directory read GET /web/api/v2.1/users?limit=... Users view/read
Site license inventory read GET /web/api/v2.1/sites?limit=... Sites view/read
Account license inventory fallback GET /web/api/v2.1/accounts?limit=... Accounts view/read
Identity read N/A Derived from users payload fields N/A
Per-user license N/A Not available in this integration N/A
Site/account SKU / billing GET Prefer /sites fields (totalLicenses, activeLicenses, expiration); fallback to /accounts fields (totalLicenses, activeAgents, skus, licenses) Sites/accounts view/read
Activity N/A Uses lastLogin when present, otherwise updatedAt/createdAt proxy N/A
Endpoint / device read GET /web/api/v2.1/agents?limit=... Endpoints/agents view/read
Installed application inventory GET /web/api/v2.1/installed-applications?limit=... Application Risk / Application Management read (Complete SKU)

5. Discovered app use (EDR)

A-Count can attribute installed desktop applications to company users by combining SentinelOne endpoint data with the existing discovered_app_use pipeline:

  1. GET /web/api/v2.1/agents — device metadata and lastLoggedInUserName / osUsername (the interactive user on the endpoint, not the management-console user).
  2. GET /web/api/v2.1/installed-applications — installed application inventory per agent (agentUuid, name, publisher, version, riskLevel).

How attribution works

  1. JumpCloud device bridge (recommended): When JumpCloud is configured in API mode, A-Count maps each SentinelOne agent to a JumpCloud system by serial number (preferred) or hostname (computerName / displayName), then resolves the corporate email from JumpCloud (primarySystemUser, or exactly one bound user on the system).
  2. OS username fallback: If JumpCloud is unavailable or no device match is found, A-Count uses the agent's last interactive user (lastLoggedInUserName / osUsername) and resolves it to a company email via synced User / ProviderAccount records.
  3. Devices whose user cannot be resolved (including JumpCloud systems with multiple bound users and no primary user) are skipped (logged, not stored).
  4. OS/system noise (drivers, runtimes, Windows updates, SentinelOne agent itself, etc.) is filtered out before signals are stored.

JumpCloud prerequisites

  • JumpCloud provider configured in API mode with a valid Admin API key (same as user sync).
  • API access to GET /api/systems and GET /api/v2/systems/{id}/users for device-to-user binding.
  • Serial numbers or hostnames should be consistent between SentinelOne agents and JumpCloud systems for reliable matching.

Stored signal metadata includes user_resolution_method (jumpcloud_serial, jumpcloud_hostname, or os_username) and jumpcloud_system_id when applicable.

Permissions beyond user sync

  • Endpoints/agents view/read for /agents.
  • Application Risk / Application Management read for /installed-applications (requires Complete SKU with Application Risk enabled on the tenant).

Limitations

  • Installed inventory only — not a live "running processes" feed.
  • Without JumpCloud, attribution uses the device's last logged-in OS user, not a full per-user history.
  • No per-user application map is native in SentinelOne; A-Count builds a best-effort (user email, application) signal.

Run discovery from Discovered Apps in the UI (POST /api/discovered-app-use/run with no source_types filter runs SSO + EDR sources).

6. License Sync

  • User sync: Yes, via SentinelOne Management API users.
  • License inventory sync: Yes, via SentinelOne Management API sites, with account fallback.
  • License row shape: one A-Count license per SentinelOne SKU/plan, for example Complete; the public bundle label such as Endpoint Security - Complete is stored as provider metadata.
  • License model (v2): catalog ref = plan_key / bundle name (lowercased). Inventory fetch uses run_provider_call for async sites/accounts APIs. User sync is assign-only when license_type / plan_key is present.
  • Expiration date: /sites[].expiration or fallback /accounts[].expiration is persisted as the license renewal/expiration date shown in the UI.
  • User to license assignment: Not native. Console users are not assigned to agent/SKU inventory rows; user sync still falls back to the generic provider license when no user-level license exists.
  • Extra permissions for license sync: site read/list permission for /web/api/v2.1/sites; account read/list permission for /web/api/v2.1/accounts fallback.

7. Installation In A-Count

  1. Open Settings.
  2. In Provider Connections, click Configure on SentinelOne.
  3. Select Import mode = API.
  4. Enter the SentinelOne URL, for example https://euce1-108.sentinelone.net.
  5. Paste the API token in API Token.
  6. Click Test.
  7. Save.

8. Known Limitations

  • last_activity_at depends on SentinelOne returning lastLogin; otherwise A-Count uses updatedAt or createdAt as a weak proxy.
  • User listing uses the token's own SentinelOne scope directly through /users.
  • License inventory is site/account-agent-based (activeLicenses or activeAgents, totalLicenses) and is not a per-console-user entitlement map.
  • Rows without email are skipped because A-Count cannot map them reliably to users.

9. Quick Troubleshooting

The Token Is Rejected

  • Verify the token is copied without the ApiToken prefix.
  • Verify the URL is the tenant management URL, not a documentation or regional landing page.
  • Verify the token owner can view users in the SentinelOne console.
  • Generate a new token for a service user and test again.

I Do Not See Any Users

  • Verify the token scope includes the account/site whose users should be synced.
  • Check whether SentinelOne requires account/site scope for your tenant.
  • Confirm the user rows include email addresses.

I Do Not See The API Option

  • Verify that the SentinelOne provider exists in the database after app startup.
  • Verify sentinelone is present in USERS_PROVIDERS.

10. Rotation & Security

  • Rotate the token regularly.
  • Revoke the old token immediately after replacing it in A-Count.
  • Keep the management URL in api_config; only the API token is treated as a secret.

Configure Slack

Quick Summary

  • Required role: Slack workspace admin
  • Where to go: api.slack.com/apps
  • What you need: a Slack app installed on the correct workspace
  • Required scopes: users:read, users:read.email, team.billing:read, team:read
  • If you add a new scope later: you must click Reinstall to Workspace
  • Installation in A-Count: paste the Bot User OAuth Token into API Token
  • Security: the token is encrypted when saved server-side

1. Prerequisites

  • Be a Slack workspace admin
  • Have access to the target Slack workspace
  • Have permission to install or reinstall apps on that workspace

2. Access The Slack App

  1. Go to api.slack.com/apps.
  2. Click the name of your application.
  3. Make sure you are editing the app connected to the correct workspace.

3. Add The Required Permissions

  1. In the left sidebar, under Features, click OAuth & Permissions.
  2. Scroll down to the Scopes section.
  3. In Bot Token Scopes, click Add an OAuth Scope.
  4. Add these scopes:
    • users:read
    • users:read.email
    • team.billing:read
    • team:read
  5. If you need extra access later, add the new scope here as well.

4. Reinstall The App To Regenerate The Token

Adding scopes is not enough on its own. Slack only updates the token permissions after a reinstall.

  1. Stay on the OAuth & Permissions page.
  2. Go back to the top of the page.
  3. Click Reinstall to Workspace.
  4. Slack will show a confirmation screen with the updated permissions.
  5. Click Allow.

5. Copy The API Token

  1. After the reinstall, return to OAuth & Permissions.
  2. Copy the Bot User OAuth Token.
  3. This is the token you must use in A-Count.

6. Install In A-Count

  1. Open Settings.
  2. In Provider Connections, click Configure for Slack.
  3. Select API mode.
  4. Paste the token into API Token.
  5. Click Test.
  6. Save.

7. What A-Count Does

  • A-Count uses the Slack token to read workspace user data.
  • The token is tested before it is saved.
  • Once valid, it is encrypted and stored in the database.

8. Quick Troubleshooting

  • invalid_auth: the token is invalid, expired, or copied incorrectly.
  • missing_scope: add the missing scope, then click Reinstall to Workspace.
  • Still not working after adding a scope: you probably updated the app but did not reinstall it.
  • Wrong workspace data: verify that the app is installed on the correct Slack workspace.

Configure Unity (SCIM API + CSV fallback)

Quick summary

  • Modes:
    • import_mode = api — users only via Unity SCIM GET /Users (licenses / seats are excluded)
    • import_mode = csv — Members / seat exports from Unity ID (use this for license visibility)
  • Auth:
    • Long-lived bearer token from a Unity service account (preferred)
    • Basic auth with service account key ID + secret
  • Prerequisites for a non-empty SCIM directory:
    • At least one validated domain on the org
    • SSO configured in Unity Dashboard
    • Enforce SSO / Enforce SCIM can stay off — a-count does not require Enforce
  • Without domains + SSO, Test/Sync often succeed with 0 users (HTTP 200 + empty Resources). That is Unity’s model, not an a-count bug — use CSV for Members until SSO/domains exist.

Official docs: Unity SCIM API.


Important limitations

API import is limited to users. a-count does not sync Unity licenses, subscription seats, or Editor entitlements in API mode. License inventory for Unity is skipped.

Source What it shows
Unity SCIM /Users (cloud.unity.com) Accounts under verified email domains (identity only)
Unity ID → Members & Groups Organization members and roles (id.unity.com)
Unity ID → Subscriptions & Services → Manage seats Real seat / license assignments — not available via SCIM or any public Unity API

a-count API sync calls the Unity SCIM Users endpoint using your org-specific SCIM base connector URL. It returns user identity fields only (id, email, display name, SCIM active). It does not return subscription seat assignments.

If you need licensed vs unlicensed Members, keep CSV import mode and upload exports from Unity ID (Members & Groups / Manage seats).


1. Required Unity setup

  1. In Unity Dashboard, switch to the target organization.
  2. Go to Administration → Service accounts and create a service account.
  3. Assign the organization role SCIM Authenticator.
  4. Create credentials:
    • Bearer (recommended): Bearer tokens → Add bearer token → copy the token
    • Basic: Keys → Create key → copy key ID and secret
  5. Go to Administration → Single sign on.
  6. In the SCIM section, copy the SCIM base connector URL.
  7. For a non-empty ListUsers response: add/validate domains and configure SSO (Enforce can remain off).

Without the SCIM Authenticator role and a valid SCIM base URL, API calls usually fail with 401 or 403.

2. Provider config (api_config)

Use Unity provider settings with:

  • unity_scim_base_url: SCIM base connector URL from Administration → SSO (required in API mode)
  • unity_auth_mode: bearer_token (default) or basic
  • unity_key_id: required in Basic mode (service account key ID)
  • Secret storage:
    • Bearer mode: long-lived bearer token in encrypted api_token
    • Basic mode: service account secret in encrypted api_token

3. Data synced (API mode)

API mode syncs users only. License / seat inventory is excluded.

  • Users:
    • SCIM user id
    • Primary email (userName / emails)
    • Display name
    • Active / inactive status from SCIM active
  • Not synced via API (use CSV / Unity ID instead):
    • Seat / subscription / Editor license assignments
    • Licensed vs unlicensed Member status from Unity ID
    • Organization roles beyond what SCIM returns
    • Last activity timestamps (not present on standard SCIM User resources)

4. Empty SCIM (0 users)

If Test connection reports success with 0 user row(s) visible:

  1. Confirm SSO is configured and at least one domain is validated.
  2. Confirm the service account still has SCIM Authenticator.
  3. Until the SCIM directory is populated, switch Unity to CSV and import Members from the Dashboard.

5. CSV fallback (required for licenses)

When import_mode = csv, Unity uses the generic CSV pipeline (same as other CSV providers). Use this for seat/license data from Unity ID (Members & Groups / Subscriptions & Services → Manage seats). SCIM API import cannot replace that export.

Configure Wiz

Quick summary

  • Default: import_mode = csv (export users from the Wiz portal).
  • API mode: GraphQL + OAuth service account (client_id:client_secret) with read:users.
  • Paste credentials: Settings → Provider Connections → Wiz → Configure → API.

Official docs: docs.wiz.io (GraphQL API / Service Accounts).


1. When to use CSV vs API

Situation Recommended mode Why
No service account / GraphQL access CSV Keep exporting from the portal
Tenant-wide SA with read:users API Lists portal users via GraphQL

If API token test fails, stay on CSV — A-Count does not auto-fallback during sync.


2. API setup

  1. In the Wiz portal, open Settings → Tenant Info → General and copy:
    • API Endpoint URL (example: https://api.eu14.app.wiz.io/graphql)
    • Authentication URL (usually https://auth.app.wiz.io/oauth/token)
  2. Open Settings → Access Management → Service Accounts → Add Service Account (type: Custom Integration / GraphQL API).
  3. GraphQL service accounts use API Scopes only (no Role picker in the UI):
    • Projects: leave empty (tenant-wide / all projects). If the form shows N selected, user listing often returns totalCount: 0 even with read:users checked, while User Management still shows people. Wiz frequently cannot change projects after create — create a new SA.
    • API scopes: enable Users → Read user details, list all users (read:users) and Licenses → Access the tenant's licensing information (read:licenses) so license inventory can sync with users.
  4. Copy Client ID and Client Secret (secret is shown once).
  5. In A-Count:
    • Set import_mode = API
    • Wiz GraphQL URL: paste the tenant API Endpoint URL
    • Credentials: client_id:client_secret
    • Click Test, then save
  6. Run user sync (POST /users/sync or scheduler) — license inventory runs first when read:licenses is present.

A-Count calls:

  • POST https://auth.app.wiz.io/oauth/token (or auth.gov.wiz.io for gov hosts) with grant_type=client_credentials and audience=wiz-api
  • POST {wiz_graphql_url} GraphQL users(first, after) with cursor pagination
  • POST {wiz_graphql_url} GraphQL tenantLicenses for ACTIVE license quotas

3. Data mapped in A-Count

Wiz field A-Count field Notes
id provider_user_key Stable Wiz user id
email email
name name
effectiveRole.name role Mapped to admin when name looks like Global Admin; else user
status status Sync excludes DELETED (same as Wiz UI default)
lastLoginAt last_activity_at
Full record raw source: wiz_graphql_api

Licenses (from tenantLicenses, status = ACTIVE only):

Wiz field A-Count field Notes
name license name e.g. Wiz Advanced, Wiz Code, Runtime Sensor
quotas[].amount / quotaUsage.totalAmount seats / total Capacity for that SKU
quotaUsage.usedAmount assigned Current usage (may exceed capacity)
quotaUsage.type quota_type SEAT_COUNT → per_user; otherwise per_license
endAt expiration / renew date Manual renew date in inventory raw
startAt subscription_started_at

Wiz does not expose purchased dollar cost on tenantLicenses (only capacity quotas such as workloads, seats, sensors, ingestion units).


4. Common errors

Symptom Likely cause
OAuth 401/403 Wrong client id/secret, or account lacks scopes
GraphQL 401/403 Wrong regional URL, or token missing read:users / read:licenses
Sync OK but 0 users while UI shows users SA is project-scoped (N selected) — create a new SA with Projects empty + read:users
Users sync but no licenses Missing read:licenses, or no ACTIVE tenantLicenses rows
GraphQL errors in body Schema/permission issue — inspect errors[].message
Invalid URL Must be https://…wiz.io… ( /graphql is appended if omitted)

Configure Zoom

Quick Summary

  • Required role: Zoom account admin (to create and activate the app).
  • Portal: Zoom App Marketplace → Develop → Build App (or Create app) → Server-to-Server OAuth → Scopes.
  • Official guide: Server-to-Server OAuth.
  • Credentials in A-Count: Account ID + token client_id:client_secret (encrypted).
  • Category: communication.

Minimum for user + license sync: create the S2S app (§2), add the 3 granular scopes in §3, Activate the app, paste Account ID + client_id:client_secret in A-Count, Test / Save, run sync.

In Marketplace, search… Scope to check Why
list_users (product User) user:read:list_users:admin List users
settings (product User) user:read:settings:admin Add-ons (Large Meeting, Webinar, …)
plan_usage (product Billing, not Accounts) billing:read:plan_usage:admin License inventory

Do not search account:read:admin — new apps only show granular scopes; that search returns unrelated Account permissions.


1. Prerequisites

Capability Requirement
Create S2S app Zoom account admin; access to Zoom App Marketplace
User sync S2S app with user:read:list_users:admin
License inventory Pro / Business / Enterprise + billing:read:plan_usage:admin (Free: users may sync, inventory often empty)
Add-on seats (Large Meeting, Webinar, …) user:read:settings:admin via GET /users/{id}/settings

2. Create the Zoom Server-to-Server app

Follow Zoom’s guide: Server-to-Server OAuth.

  1. Open the Zoom App Marketplace (sign in as account admin).
  2. Go to Develop → Build App, or open Create app directly.
  3. Choose Server-to-Server OAuth (not OAuth, not JWT / legacy).
  4. Name: e.g. A-Count.
  5. Complete basic information if prompted (company name, developer contact email).
  6. Open the Scopes tab and add the permissions in §3.
  7. Click Continue / Activate. Without activation, token calls fail (admin approval may be required).

Copy credentials for A-Count

On the app App Credentials page:

Zoom field Where it goes in A-Count
Account ID Dedicated Zoom Account ID field (api_config.zoom_account_id)
Client ID First part of the token
Client Secret Second part of the token

Paste the token as: client_id:client_secret (two parts, colon-separated, no spaces). Do not put the Account ID inside the token — it has its own field.


3. Scopes (permissions)

New Marketplace apps use granular scopes only (reference). Classic names like user:read:admin / account:read:admin usually do not appear in Add Scopes.

Still in the app: Scopes → Add Scopes.

Exact scopes to add (check these three)

Product (left sidebar) Search box Checkbox label (typical) Scope ID
User list_users View users / List users user:read:list_users:admin
User settings View a user's settings user:read:settings:admin
Billing plan_usage Get plan usage / account subscriptions usage billing:read:plan_usage:admin

How to pick them in the UI:

  1. Left sidebar → User → search list_users → check user:read:list_users:admin.
  2. Still under User → search settings → check user:read:settings:admin (not account lock settings).
  3. Left sidebar → Billing (not Accounts) → search plan_usage → check billing:read:plan_usage:admin.
  4. Click Done, then Activate / re-activate the app.
Scope Used for
user:read:list_users:admin List users (GET /v2/users) — required for user sync
user:read:settings:admin User settings / add-ons (GET /v2/users/{id}/settings)
billing:read:plan_usage:admin Plan usage / license inventory (GET /v2/accounts/me/plans/usage) — required for license sync

Avoid: scopes under Accounts such as account:read:entitlement:admin, account:read:lock_settings:admin, account:read:meeting_template_detail:admin — they are not what A-Count uses.

Legacy note: older apps may still use classic user:read:admin + account:read:admin. Prefer the granular trio above for new apps.

Do not create a user-managed OAuth or JWT app for A-Count — only Server-to-Server OAuth with grant_type=account_credentials is supported.


4. Configure in A-Count

  1. Open Settings → Provider Connections.
  2. Find Zoom → Configure.
  3. Select API Sync mode.
  4. Paste the Account ID.
  5. In the credentials field, enter client_id:client_secret.
  6. Click Test, then Save.

Once configured, Zoom users and license inventory sync during scheduled syncs.


5. What Gets Synced

Users

Field Source
Email email
Name display_name, else first_name + last_name
Role role_id (0=owner, 1=admin, else user)
Status status (active / inactive / pending)
Last activity last_login_time
Provider user key Zoom user id
License tier type=1 → Zoom Meetings Basic; type=2 / license_info_list MEETING → Zoom Workplace Pro

License inventory

From /accounts/me/plans/usage, each plan_* bucket with contracted seats becomes an inventory row, for example:

Plan key Display name
plan_base Zoom Workplace Pro
plan_zoom_rooms Zoom Rooms
other plan_* Derived from key

Each row stores contracted (hosts) counts. For Zoom Workplace Pro (plan_base), Zoom's usage is stored as assigned seats (and matches linked users). Add-on plans (Large Meeting, Webinar, …) keep vendor usage separately; user primary seat binds via license_type → catalog ref=plan_base (P3 catalog / inventory-driven). Contracted seats for add-ons remain inventory-only (UI assigned counts come from linked users / license_usages).


6. Rollout checklist

  1. Create the S2S app (§2).
  2. Add the 3 granular scopes (§3): user:read:list_users:admin, user:read:settings:admin, billing:read:plan_usage:admin; Activate.
  3. Copy Account ID + Client ID + Client Secret.
  4. Configure Zoom in A-Count (§4); Test then Save.
  5. Run (or wait for) scheduled user + license sync.
  6. Confirm users appear and license inventory shows plan_* rows (Pro+ account).

7. Known Limitations

  • Per-user type maps to portal names: Basic (1) and Workplace Pro (2); not full SKU/pricing breakdown.
  • license_info_list is returned only for some accounts (multiple/duplicate subscription plans).
  • Add-on seats (Large Meeting, Webinar, …) are read from GET /users/{id}/settings during sync.
  • Access tokens expire after one hour; A-Count caches and refreshes them automatically.
  • Rate limits: Zoom enforces per-app rate limits; A-Count retries on HTTP 429.

8. Troubleshooting

Error Cause Fix
401 Unauthorized Bad credentials, deactivated app, or wrong Account ID Recreate or re-copy credentials from Marketplace; confirm Account ID; ensure app is Activated
403 Forbidden Missing scopes Add user:read:list_users:admin (+ settings / plan_usage as needed) → re-activate
Account ID required zoom_account_id not saved Set Account ID in Settings and Save
Invalid credential format Token not client_id:client_secret Paste both values separated by a single colon (Account ID stays in its own field)
No plan usage / empty inventory Free plan or missing billing:read:plan_usage:admin Upgrade plan; under Billing add billing:read:plan_usage:admin, then re-activate
Wrong scopes in UI Searched account:read:admin under Accounts Use User + Billing products and the granular IDs in §3

Provider API rights reference

Field Value
Document version 1.9
Last reviewed 2026-09-09
Audience Operators configuring providers; developers adding integrations
Related

This document is the master inventory of API permissions, scopes, credential types, and admin roles A-Count requires from third-party providers. Use it when onboarding a new provider or auditing least-privilege access.

Operator setup (step-by-step) lives in per-provider files under backend/docs/providers/ and is served in the UI via GET /api/providers/docs/{provider_slug}.

Canonical user-sync registry: PROVIDER_REGISTRY in backend/data_providers/registry.py (exposed as USERS_PROVIDERS). Dual-mode providers may remain in CSVProviderName for CSV fallback; supports_api follows the registry.

License inventory sync: backend/routers/license_sync.py (providers with explicit branches only).


1. Universal capability model

A-Count integrations are read-only today. No provider sync requires write, delete, or admin mutation API rights.

Map every new provider to these capability buckets:

Capability Typical API surface Required?
Directory read List users / members / seats Yes (core user sync)
Identity read Email, name, login, external ID Yes (correlation / anomalies)
Role / membership type read Admin, owner, guest, suspended Yes (most providers)
Per-user license read Seat / SKU per user Often (user sync enrichment)
Org license / SKU inventory read Subscribed products, seat counts If license sync is wired in license_sync.py
Org billing / plan read Workspace plan, billing tier Optional (Slack, GitHub org plan)
Sign-in / activity read Last login, usage events Optional (Microsoft, Cursor, GitHub, Grammarly, Canva)
Security posture read 2FA / MFA status Optional (GitHub)
SCIM / IdP identity read SAML email, externalId Optional (GitHub SCIM, Miro SCIM, Figma SCIM, Unity SCIM)

Not used by A-Count today: webhooks (except Google OAuth callback), inbound vendor callbacks, GCP service-account JSON for Workspace (Google uses admin OAuth consent, not a robot service account).


2. Auth model decision tree

Choose one primary model when documenting a new provider:

Does the vendor support OAuth 2.0?
        ├── Admin must sign in interactively (Workspace / directory APIs)
        │   └── OAuth 2.0 authorization code + refresh token (Google)
        ├── Machine-to-machine only (no user browser)
        │   └── OAuth 2.0 client credentials (Microsoft Graph, Zoom S2S, Grammarly, Canva Admin, Wiz, Autodesk, Looker, 1Password)
        └── App installed on org/workspace with generated token
            └── OAuth app token / Bot token (Slack, Miro, Dropbox)

        Is it a GitHub-style App with JWT + installation token?
        └── GitHub App (App ID + PEM → installation access token)

        Is it SCIM directory read?
        └── Bearer (or Basic) SCIM token + tenant/base URL (Figma, Unity)

        Otherwise
        ├── API key in header or Basic Auth (Monday, Cursor, Dropbox Sign, JetBrains, HubSpot, Claude/OpenAI compliance keys)
        ├── Service account JSON (GCP)
        └── HTTP Basic service user (HiBob)
        

3. Credential storage in A-Count

Auth model Providers Stored where Encryption
OAuth 2.0 (admin user + refresh) Google Workspace google_oauth_client_json_encrypted, google_oauth_token_json_encrypted on Provider row MASTER_KEY
GitHub App (JWT → installation token) GitHub github_app_id, github_org, encrypted_private_key MASTER_KEY
OAuth 2.0 client credentials Microsoft 365, Looker, 1Password, Grammarly, Canva, Wiz, Zoom, Autodesk (client+PAT) Provider.api_token as composite (tenant:client:secret, client_id:client_secret, …) + optional api_config ENCRYPTION_KEY
Bearer access token Slack, Miro, Dropbox, HubSpot Provider.api_token ENCRYPTION_KEY
SCIM bearer (+ tenant/base URL) Figma, Unity Provider.api_token + api_config (figma_tenant_id, unity_scim_base_url, …) ENCRYPTION_KEY
Compliance / admin API key Claude, OpenAI Provider.api_token (+ openai_workspace_id for OpenAI) ENCRYPTION_KEY
API key / Basic Monday, Cursor, Dropbox Sign, JetBrains, Atlassian, Parsec Provider.api_token (JetBrains: api_key:customer_code; Atlassian/Parsec: + org/team id in api_config) ENCRYPTION_KEY
HTTP Basic (service user) HiBob Provider.api_token (Base64 of serviceUserId:token) ENCRYPTION_KEY
OAuth 1.0 TBA composite NetSuite Provider.api_token as account_id:consumer_key:consumer_secret:token_id:token_secret ENCRYPTION_KEY
AWS Roles Anywhere (recommended) or access key (+ optional AssumeRole) AWS Roles Anywhere: encrypted private key PEM in api_token + cert/ARNs in api_config; legacy: AccessKeyId:SecretAccessKey ENCRYPTION_KEY
GCP service account JSON GCP Dedicated SA JSON fields / upload (not generic token paste) Encrypted storage
Env-only SCIM (optional) Miro MIRO_SCIM_TOKEN Environment variable
Dual-mode CSV + API Autodesk, Claude, Figma, Grammarly, OpenAI, Unity, Wiz, Adobe, Atlassian, Parsec, AWS API creds when import_mode=api; CSV upload when csv See API row
Local Settings directory a-count.io No stored secret; users come from Settings → Users management N/A
CSV / manual Stay-CSV vendors (see §4 / §8) No API secret N/A

Remote access = outbound HTTPS from the A-Count API host to vendor endpoints using the credential above. Document base URL, auth header shape, and endpoints per provider (see §5).


4. Per-provider rights matrix

Summary table

Provider Auth Exact rights (minimum) License sync Operator doc
Google Workspace OAuth 2.0 admin admin.directory.user.readonly, apps.licensing Yes google-workspace.md
GitHub GitHub App Org: Members Read+Write, Administration Read; Repo Read (optional) Plan snapshot only github.md
Microsoft 365 Client credentials User.Read.All; + Organization.Read.All for SKU sync Yes microsoft.md
Slack Bot OAuth token users:read, users:read.email, team.billing:read, team:read Yes slack.md
Miro OAuth access token organizations:read, identity:read, team:read Yes miro.md
Dropbox Business Bearer token members.read (team API) No dropbox.md
HiBob HTTP Basic service user People / Employee read (UI permissions) No hibob.md
Monday.com Personal API token GraphQL users read (no OAuth scope list) Yes monday.md
JetBrains X-Api-Key + X-Customer-Code Account API enabled; read licenses endpoint Yes jetbrains.md
Cursor Admin API key (Basic) Team Admin API key No cursor.md
Mixpanel Service Account (HTTP Basic) Org Owner/Admin service account; optional SCIM Bearer (MIXPANEL_SCIM_TOKEN) No (infrastructure billing) mixpanel.md
NetSuite OAuth 1.0 TBA (HMAC-SHA256) SuiteQL + Employee read; composite token in api_token No (generic provider license only) netsuite.md
Dropbox Sign API key (Basic) Paid plan + admin role (no OAuth scopes) Yes dropbox-sign.md
SentinelOne API token (ApiToken header) Users view/read; sites/accounts view/read for license sync; agents view/read + Application Risk read for EDR app discovery Yes sentinelone.md
ESET OAuth2 password grant (cloud) or /GetTokens / Inspect authenticate (on-prem) Device Management GET /v1/devices (cloud / on-prem PROTECT); Inspect computers list (on-prem Inspect). Dedicated API user with Integrations; Superuser cannot be the API user Yes (observed product seats from devices) eset.md
JumpCloud Admin API key (x-api-key header) System users read; systems read; applications read; System Insights read; v2 graph read No jumpcloud.md
1Password OAuth 2.0 client credentials List users (Partners Users API); client_id:client_secret + account ID + region No onepassword.md
Looker API3 client credentials Looker admin; client_id/client_secret + instance base URL No looker.md
GCP Service account JSON Resource Manager / IAM / Asset / Policy Analyzer roles (read) Yes (synthetic package) gcp.md
HubSpot Private App bearer settings.users.read No hubspot.md
Zoom S2S OAuth user:read:admin, account:read:admin; Account ID Yes zoom.md
Claude Compliance Access Key read:compliance_org_data, read:compliance_user_data (Enterprise) No claude.md
OpenAI Compliance API key Compliance read scopes + openai_workspace_id (ChatGPT Enterprise) No (default ChatGPT seat) openai.md
Grammarly OAuth client credentials users-api:read (License Management API); Enterprise/Education Yes grammarly.md
Canva OAuth client credentials (Basic) admin:user:read (Admin API); Enterprise org only No canva.md
Wiz OAuth GraphQL SA read:users; + read:licenses for inventory Yes wiz.md
Figma SCIM bearer + Tenant ID Org/Enterprise SCIM Users read Yes (seat types from roster) figma.md
Unity SCIM bearer/basic + SCIM base URL SCIM Authenticator; ListUsers No (licenses stay CSV) unity.md
Autodesk OAuth client + PAT (or static token) Insights exports; scopes data:read data:write Yes autodesk.md
Adobe OAuth S2S (IMS) user_management_sdk + org id; Client ID in api_config, secret in api_token Yes (product profiles) adobe.md
Atlassian Org API key (Bearer) Org Admin + Guard + verified domain for emails; product access from Admin users API Yes (assigned seats) atlassian.md
Parsec Teams API key (Bearer) Enterprise only; Public API key + Team ID; View Team Members Yes (active member seats) parsec.md
AWS Pod / instance role (IRSA), AssumeRole (optional key), or Roles Anywhere (outside AWS) Identity Store ListUsers; CE GetCostAndUsage; Budgets DescribeBudgets Yes (infrastructure spend) aws.md
a-count.io Local Settings users (no vendor token) Read this instance’s application users (User table) No acount.md

Google Workspace

Auth OAuth 2.0 — Workspace admin consents in browser
OAuth scopes https://www.googleapis.com/auth/admin.directory.user.readonly — user directory
https://www.googleapis.com/auth/apps.licensing — license assignments
APIs to enable Admin SDK API, Enterprise License Manager API — required for license sync; else 403 accessNotConfigured
Provider admin role Google Workspace super/admin
A-Count storage Encrypted OAuth client JSON + authorized-user token (not api_token)
User sync Yes
License sync Yes

GitHub

Auth GitHub App installation token (PAT save disabled in Settings)
App permissions Organization: Members Read+Write (Write required for Anomalies remote remove; teams are included), Administration Read, Plan Read, GitHub Copilot Business Read (/copilot/billing and /copilot/billing/seats), Organization Copilot metrics Read
Repository: Metadata Read, Contents Read (private-repo last activity)
Setup: github.md §2
Legacy PAT scopes (code only) read:org, admin:org (SCIM), repo (private repo activity)
Optional SCIM GET /scim/v2/organizations/{org}/Users when GITHUB_SCIM_ENRICHMENT=1
Provider admin role Org Owner/Admin; org owner for 2FA-disabled filter
A-Count storage App ID, org slug, encrypted PEM
User sync Yes
License sync Org plan snapshot (api_config.github_license) plus Copilot add-on (api_config.github_copilot, seats from /copilot/billing/seats). 28-day Copilot metrics reports are not imported; the enterprise policy Copilot usage metrics must be Enabled everywhere before that API answers (Copilot usage metrics)

Microsoft 365 (Graph)

Auth App registration; token scope https://graph.microsoft.com/.default
Application permissions Minimum: User.Read.All
License sync: Organization.Read.All (GET /v1.0/subscribedSkus)
Recommended: Directory.Read.All, GroupMember.Read.All, Reports.Read.All
Optional Per-user licenseDetails; signInActivity on /users; memberOf for team scope; reports fallback
Provider admin role Entra Global Administrator (admin consent)
Token format in UI tenant_id:client_id:client_secret
User sync Yes
License sync Yes

Slack

Auth Bot User OAuth Token
Bot scopes users:read, users:read.email, team.billing:read, team:read
Reinstall required After adding scopes — Reinstall to Workspace
Provider admin role Workspace admin
User sync Yes
License sync Yes (team.billing.info)

Miro

Auth OAuth access token; optional MIRO_SCIM_TOKEN
App scopes organizations:read, identity:read, team:read
Plan / role gates Enterprise + Company Admin
Reinstall required After scope changes
User sync Yes (org members and/or SCIM)
License sync Yes

Dropbox Business

Auth OAuth access token (Bearer)
Team permission members.read (for teams/members/list)
Provider admin role Dropbox Business team admin
User sync Yes
License sync No

HiBob

Auth HTTP Basic — Service User ID + Token
HiBob UI permissions People / Employee read APIs
Provider admin role HiBob admin (create Service Users)
User sync Yes
License sync No

Monday.com

Auth Personal API token (GraphQL), API-Version: 2026-07
Rights Token must read GraphQL users (status/kind), account.products/tier, apps_monetization_info; no OAuth scope list
Provider admin role Monday admin recommended
Optional SCIM Enterprise only — env MONDAY_SCIM_TOKEN (+ MONDAY_SCIM_DOMAIN); enriches userType, not product seats
User sync Yes
License sync Yes (account tier + product kinds; guest/viewer seat class from user kind)
Setup doc monday.md

JetBrains

Auth Headers X-Api-Key, X-Customer-Code
Token in A-Count api_key:customer_code (or \|) in API Token
Provider setup Central Console → Enable API; verified domain
Endpoint GET https://account.jetbrains.com/api/v1/customer/licenses
User sync Yes
License sync Yes

Cursor

Auth Admin API key — HTTP Basic (key as username)
Rights Team Admin API key (UI may show admin:*-style labels)
Provider admin role Cursor team administrator
User sync Yes
License sync No

Mixpanel

Auth Service Account — HTTP Basic username:secret in Provider.api_token
Optional SCIM Bearer token in server env MIXPANEL_SCIM_TOKEN (Enterprise SSO)
Endpoints GET /api/app/me (unified_member_list); optional GET /api/app/scim/v2/Users
Provider admin role Organization Owner or Admin for the service account
User sync Yes (humans + service accounts)
License sync No (infrastructure billing; default license name on accounts)

Dropbox Sign (HelloSign)

Auth API key — Basic Auth (no OAuth scopes)
Access rules Paid plan (Standard+); admin role for /team
User sync Yes
License sync Yes

SentinelOne

Auth API token — Authorization: ApiToken <token>
Token in A-Count Provider.api_token (encrypted); api_config.sentinelone_base_url (tenant management URL)
Minimum (user sync) GET /web/api/v2.1/users — users view/read
License sync GET /web/api/v2.1/sites (preferred) or /accounts — sites/accounts view/read
EDR discovered app use GET /web/api/v2.1/agents (endpoints view/read); GET /web/api/v2.1/installed-applications (Application Risk / Complete SKU)
EDR attribution bridge Optional JumpCloud MDM — see jumpcloud.md
Provider admin role SentinelOne administrator or scoped service user
User sync Yes (management console users)
License sync Yes (site/account agent inventory)
Discovered app use Yes (installed apps attributed via JumpCloud device bridge and/or OS username fallback)

ESET

Auth Cloud: OAuth 2.0 password grant against {region}.business-account.iam.eset.systems. On-prem PROTECT: POST /GetTokens. On-prem Inspect: POST /api/v1/authenticate.
Token in A-Count Provider.api_token stores the encrypted API user password. api_config.eset_username, eset_mode, eset_region (cloud), eset_base_url (on-prem), optional eset_verify_ssl.
Minimum (connection test + devices) Cloud: Integrations enabled + Device Management GET /v1/devices. On-prem PROTECT: API permission set that can list devices (not Administrator, not 2FA). On-prem Inspect: computers list.
License sync Observed seats from device activeProducts or deployed security components. Cloud also reads installer catalog for product/subscription identity. Purchased Hub pool size and unit price are not in the API.
User sync No SaaS member directory. Combined sync stores zero accounts. CSV staged data still works.
EDR discovered app use Not in this integration. Public APIs do not return installed third-party apps per device.
Provider admin role Dedicated API user (not Superuser). Cloud: Integrations on, invitation completed, logged in once.
User sync No
License sync Yes (device product activations as observed seats)
Discovered app use No

JumpCloud

Auth Admin API key — x-api-key header
Token in A-Count Provider.api_token (encrypted)
Minimum (user sync) GET /api/systemusers — system user read
EDR device bridge GET /api/systems; GET /api/v2/systems/{id}/users when primary user unset
SSO app discovery GET /api/applications; GET /api/v2/applications/{id}/users (applications.readonly)
Installed app discovery GET /api/v2/systeminsights/apps, /programs (System Insights enabled)
Optional (activity) GET /api/v2/users/{id}/systems, GET /api/systems/{id}
Provider admin role JumpCloud administrator with API key access
User sync Yes
License sync No
Discovered app use Yes (SSO entitlements, System Insights inventory, SentinelOne EDR device bridge)

1Password

Auth OAuth 2.0 client credentials
Token in A-Count client_id:client_secret + api_config.onepassword_account_id / region
Rights List users (Partners Users API public preview)
Gate Unsupported when automated SCIM provisioning is enabled on the account
User sync Yes
License sync No

Looker

Auth API3 OAuth client credentials (POST /api/4.0/login)
Token in A-Count Base URL + client id/secret (JSON / dialog fields)
Provider admin role Looker administrator (service account user with API3 key)
User sync Yes
License sync No

GCP

Auth Google Cloud service account JSON
Rights Resource Manager / IAM / Cloud Asset / Policy Analyzer read roles; OAuth scope cloud-platform
User sync Yes (IAM principals + optional SA activity)
License sync Yes — synthetic Google Cloud Platform package (per_company; cost manual)
Setup doc gcp.md

HubSpot

Auth Private App access token (Bearer)
Scope settings.users.read
Provider admin role HubSpot Super Admin
User sync Yes
License sync No

Zoom

Auth Server-to-Server OAuth (account_credentials)
Scopes user:read:admin, account:read:admin
Token in A-Count client_id:client_secret + Account ID in api_config
User sync Yes
License sync Yes (/accounts/me/plans/usage)

Claude (Anthropic)

Auth Compliance Access Key (sk-ant-api01-…); Enterprise only
Scopes read:compliance_org_data, read:compliance_user_data
Dual-mode Default CSV; API when Compliance key works
User sync Yes
License sync No

OpenAI (ChatGPT)

Auth Compliance-scoped Platform key + openai_workspace_id
Plan gate ChatGPT Enterprise
Dual-mode Default CSV; API when Compliance scopes granted
User sync Yes
License sync No (members get default ChatGPT seat label)

Grammarly

Auth OAuth 2.0 client credentials (client_id:client_secret)
Scope users-api:read (License Management API)
Plan gate Enterprise / Education institution
Endpoints /ecosystem/api/v1/users, /invitees, /institutions-summary
User sync Yes (incl. last_activity_at)
License sync Yes

Canva

Auth OAuth 2.0 client credentials (HTTP Basic + grant_type=client_credentials)
Scope admin:user:read (Admin API)
Plan gate Canva Enterprise organization only (Free / Pro / Teams / Business: no Admin API)
Role Organization admin (+ MFA or SSO to create clients)
Token in A-Count client_id:client_secret in Provider.api_token
Endpoints POST /auth/v1/oauth/token, GET /admin/v1/users
User sync Yes (incl. last_active_at)
License sync No (no Free/Pro/SKU/seats via Admin API)

Wiz

Auth OAuth client credentials GraphQL service account
Scopes read:users (minimum); read:licenses for inventory
Token in A-Count client_id:client_secret + GraphQL URL in api_config
User sync Yes
License sync Yes (tenantLicenses, ACTIVE only)

Figma

Auth SCIM bearer token + Tenant ID
Plan gate Organization / Enterprise with SCIM (+ SAML SSO)
Endpoint GET https://www.figma.com/scim/v2/{tenant_id}/Users
User sync Yes (SCIM-bound users)
License sync Yes (seat types aggregated from roster)

Unity

Auth SCIM bearer (preferred) or Basic; SCIM base connector URL
Role Organization SCIM Authenticator
User sync Yes (identity only; may be empty without verified domains + SSO)
License sync No — seats stay CSV (not exposed on SCIM)

Autodesk

Auth OAuth2 client credentials + Personal Access Token (preferred), or static 3-legged bearer
Scopes data:read data:write (Insights exports)
Dual-mode API via Business Success Plan Reporting; CSV fallback
User sync Yes (USERS export)
License sync Yes (SUBSCRIPTIONS export)

Adobe

Auth OAuth 2.0 Server-to-Server via Adobe IMS
Scopes openid, AdobeID, user_management_sdk
Token in A-Count api_config.adobe_client_id + encrypted Client Secret in api_token + api_config.adobe_org_id
Dual-mode Default CSV; API when UMAPI credentials work
User sync Yes
License sync Yes (PRODUCT_PROFILE groups / seat quotas)
Setup doc adobe.md
### Atlassian
Auth Organization API key — Authorization: Bearer
Token in A-Count Encrypted api_token + api_config.atlassian_org_id
Minimum (user sync) Org Admin API key; GET /admin/v2/orgs/{orgId}/directories + …/users
Email visibility Atlassian Guard + verified domain (otherwise emails are private/null)
License sync Aggregated assigned seats from productAccess keys
Provider admin role Organization admin
User sync Yes (dual-mode; CSV fallback)
License sync Yes (assigned product seats; not billing spare inventory)
Dual-mode Default CSV

Parsec

Auth Teams API key — Authorization: Bearer
Plan gate Enterprise only (pricing — Teams API not on Individuals/Teams tiers)
Token in A-Count Encrypted api_token + api_config.parsec_team_id
Minimum (user sync) Enterprise Public API key with View Team Members; GET /v1/teams/{teamID}/members
Activity last_connected_at → last_activity_at
License sync Roster-derived assigned seats (active members → Parsec Teams)
SCIM IdP → Parsec push only; members still appear on Teams API (is_scim)
Provider admin role Super Admin for Public API key creation
User sync Yes (dual-mode; CSV fallback when not Enterprise)
License sync Yes (assigned seats; not purchased spare inventory)
Dual-mode Default CSV
Setup doc parsec.md

CSV-only providers

No API rights. Manual CSV / staged import per remaining CSVProviderName members that are not in PROVIDER_REGISTRY. Export permissions may still apply on the vendor side.

Stay-CSV (no usable admin inventory API today): embrace, ironsource, sensortower, avo, rundiffusion, maxon, secusysdoors.


5. Endpoint inventory template (new providers)

When adding a provider, copy this table into the operator doc and fill it in:

Capability Method Path / operation Permission / scope required
Directory read
Identity read
Per-user license
Org SKU / billing
Activity

Use three tiers in operator docs (Microsoft is the reference):

Tier Meaning Example (Microsoft)
Minimum User sync works User.Read.All
Recommended Richer roles, activity, admin detection Directory.Read.All, Reports.Read.All
License sync Extra permission if license inventory is enabled Organization.Read.All
Optional Feature flags / env toggles signInActivity, GitHub repo read for activity

7. Adding a new API provider (checklist)

  1. Map capabilities (§1) — what does A-Count need to read?
  2. Pick auth model (§2) and document exact scope/permission strings.
  3. Document provider admin role and plan/tier gates.
  4. Document A-Count storage (§3) and Settings UI fields.
  5. Fill endpoint table (§5); wire PROVIDER_REGISTRY / USERS_PROVIDERS and optionally license_sync.py.
  6. Add the slug to APIProviderName (keep in CSVProviderName only if dual-mode CSV remains).
  7. Add a row to §4 summary table in this file.

8. Remaining CSV → API roadmap (Aug 2026)

From the Jul 29 feasibility assessment: grammarly, wiz, openai, figma, and unity shipped. Remaining candidates:

Build next (strong fit)

Priority Provider Auth target Notes / implementation plan
✓ shipped Adobe UMAPI OAuth S2S Dual-mode users + product-profile licenses — adobe.md
✓ shipped Atlassian Org API key + Guard Dual-mode users + assigned product seats — atlassian.md
✓ shipped Parsec Teams API key (Enterprise only) Dual-mode members + assigned seats (last_connected_at) — parsec.md
✓ shipped AWS Pod / instance role (IRSA), Roles Anywhere, AssumeRole, access key Identity Center users + CE/Budgets inventory — aws.md
✓ shipped a-count.io Local Settings users (no token) Application users as provider accounts — acount.md
3 Braze, TestRail SCIM / API key Dashboard seats (Braze); get_users (TestRail) —
4 Apple Store, Google Play, Facebook ASC JWT / androidpublisher / Graph Store/ads admin users (lower SAM value) —

Stay CSV

embrace, ironsource, sensortower, avo, rundiffusion, maxon, secusysdoors.


Document history

Version Date Summary
1.9 2026-09-24 Canva Admin API user sync (Enterprise org; no license inventory)
1.8 2026-08-21 a-count.io local Settings users as a first-party SaaS provider
1.6 2026-08-05 AWS Identity Center users + Cost Explorer/Budgets infrastructure inventory
1.7 2026-08-09 AWS IAM Roles Anywhere auth mode (no IAM users)
1.5 2026-08-05 Clarify Parsec Teams API is Enterprise-only (pricing gate)
1.4 2026-08-04 Parsec Teams API dual-mode connector (members + assigned seats; last_connected_at)
1.3 2026-08-04 Atlassian Org Admin API dual-mode connector (users + assigned product seats)
1.2 2026-08-04 Adobe UMAPI dual-mode connector (users + product-profile licenses)
1.1 2026-08-04 Add post–May 2026 providers; credential matrix; §8 CSV→API roadmap
1.0 2026-05-18 Initial master API rights reference