Provider documentation
Connect a service in Settings, then Provider Connections. Each guide below has the exact steps, permissions, and token format.
35 providers
No providers match that search.
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
- Sign in to your 1Password account on 1Password.com (or
.ca/.eu). - Open Integrations in the sidebar (then Directory if needed).
- Select OAuth Application.
- 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.
- Application name: e.g.
- Generate credentials, copy the client ID and client secret (secret is shown once), and save them in 1Password.
- 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 underassigned_until_removed)
6. Installation In A-Count
- Open Settings → Provider Connections.
- Find 1Password and click Configure.
- Select API import mode.
- Enter Account ID and Region (
com/ca/eu). - Paste
client_id:client_secretinto the credentials field. - Click Test, then Save.
7. What Gets Synced
| Field | Source |
|---|---|
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}returnslicenses_upserted: 0. Combined sync does not skip. First user sighting materializes packageref=acountnamed a-count.io and assigns every Settings user to it. - Extra permissions for license sync: N/A
6. Installation In A-Count
- Open
Settings. - Confirm people exist under Users management.
- In
Provider Connections, open a-count.io (import mode is API). - Click
Test(optional) thenSaveif you changed anything. - 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
adminare stored as provider roleuser; the original role is inraw.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
usertable exists - Restart the backend after deploying this provider so
init_providerscan seed the row
I Do Not See The API Option
- Verify the provider slug is
acountinPROVIDER_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
- Open Adobe Developer Console → Create project → Add API → User Management API.
- Choose OAuth Server-to-Server.
- Copy Client ID and Client Secret.
- Ensure scopes include
openid,AdobeID, anduser_management_sdk. - 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_PROFILEgroup (productNamepreferred, elsegroupName); seats fromlicenseQuota, assigned frommemberCount - 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
- Open Settings → Provider Connections → Adobe → Configure.
- Select Import mode = API.
- Paste Organization ID, Client ID, and Client Secret as separate fields.
- Click Test, then Save.
- 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_atstays empty) - Groups endpoint is heavily throttled (≈5 req/min); sync paces calls and retries
429 licenseQuotamay 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
- Confirm Guard is active and your company domain is verified in Admin Hub.
- Create an Organization API key (Settings → API keys) as an org admin.
- Copy the Organization ID from the Admin Hub URL.
- In A-Count Settings → Atlassian → API: paste Org ID + API key → Test → Save.
- 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 = csvfallback 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
- Create an APS app in Autodesk Platform Services with access to Business Success Plan Reporting / Insights.
- Copy Client ID and Client Secret.
- As a team admin, open Autodesk Account → Profile → Security → Personal access tokens.
- Generate a PAT for Premium Reporting / Business Success Plan, linked to your APS Client ID.
- Confirm the team (e.g. SuperPlay) has Premium benefits in manage.autodesk.com.
- In a-count Settings → Autodesk:
- Client ID / Client Secret
- PAT
- Optional Team name (
SuperPlay) or Team ID (InsightscontextId) - 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) orstatic_tokenautodesk_client_id: APS Client IDautodesk_pat: encrypted Personal Access Token (stored server-side)autodesk_team_id: optional Insights context/team idautodesk_team_name: optional team alias match (e.g.SuperPlay)autodesk_scopes: defaultdata:read data:writeautodesk_base_url: optional, defaulthttps://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
USERSexport (email, name, role/status when present, product assignments when present) - Licenses: Insights
SUBSCRIPTIONSexport (product/offering name, seat quantity / assigned — e.g. Maya 57 seats) - License model (v2): catalog
ref= Autodeskproduct_idwhen 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:AssumeRoleinto a reader role - Fallback: direct access keys (least preferred when DevOps forbids IAM users)
- Paste into the app:
Settings→Provider Connections→Configure→APImode - 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:
- Reader role — Identity Center list + Cost Explorer / Budgets
- IAM Roles Anywhere Trust Anchor + Profile that can assume that role for an A-Count client certificate
- Client certificate + private key issued against that trust anchor (handed to A-Count operators)
3.1 Prerequisites check
- 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).
- Console → IAM Identity Center → confirm it is enabled and users exist.
- 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. - Optional: Billing → Cost Explorer (enable if prompted) and Budgets if you want spend inventory.
3.2 Create the read-only policy
- Console → IAM → Policies → Create policy → JSON.
- 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": "*"
}
]
}
- Name it e.g.
ACountAwsReadOnly→ Create policy.
3.3 Create the reader role
- IAM → Roles → Create role.
- 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"
}
}
}
]
}
- Attach policy
ACountAwsReadOnly. - Role name e.g.
ACountReadOnlyRole→ Create role. - Copy the Role ARN.
3.4 Configure IAM Roles Anywhere
- Console → IAM → Roles Anywhere (or IAM → Access management → Roles Anywhere).
- Create a Trust Anchor pointing at your CA (AWS Private CA or external CA whose roots Roles Anywhere should trust).
- Create a Profile that:
- Allows the reader role from §3.3
- Optionally constrains session policies further
- Copy Trust Anchor ARN and Profile ARN.
- 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):
- Pod/instance role identity policy: allow
sts:AssumeRoleandsts:TagSessionon the reader Role ARN. - Reader role trust policy: Principal = pod role ARN; Actions =
sts:AssumeRoleandsts:TagSession(IRSA sessions carry OIDC/session tags during role chaining). - 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→ readerarn:aws:iam::127494504170:role/ACountReadOnlyRole.
- Pod/instance role identity policy: allow
- AssumeRole with IAM user caller: caller IAM user with only
sts:AssumeRoleinto the reader role + External ID; pasteAccessKeyId:SecretAccessKey(only if IAM users are allowed). - Access key (direct): attach
ACountAwsReadOnlyto one IAM user (least preferred).
Many DevOps teams forbid IAM users entirely — use Pod / instance role or Roles Anywhere (§3.2–3.5) instead.
3.8 Reinstall / consent refresh
- Not applicable (no OAuth app install). Rotate client certificates / private keys periodically.
3.9 After IAM fix — retest checklist
- Settings → AWS → API → AssumeRole, Role ARN set, access key blank → Test.
- Expect success (Identity Center sample users) or a permission error on Identity Store / CE — not
sts:TagSession. - 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
- Open
Settings. - In
Provider Connections, clickConfigureon AWS. - Select
Import mode = API. - 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.
- Click
Test, thenSave. - 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_secretin 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
- Sign in to Canva as an Organization admin.
- Profile → Settings → under Organization settings → Admin API.
- Create an API client → name it (e.g.
A-Count). - Select scopes (minimum):
admin:user:read— required for user sync
- Save Client ID and Client secret (secret shown once).
- In A-Count: set
import_mode = API, pasteclient_id:client_secret, click Test, then save. - Run user sync.
A-Count calls:
POST https://api.canva.com/auth/v1/oauth/token(Basic Auth +grant_type=client_credentials, scopeadmin:user:read)GET https://api.canva.com/admin/v1/users(pagination viacontinuation)
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
- In claude.ai admin, request/enable the Compliance API and create a Compliance Access Key (
sk-ant-api01-...). - Scopes required for user sync:
read:compliance_org_data— list organizationsread:compliance_user_data— list users per organization
- In A-Count: set
import_mode = API, paste the key, click Test token. - Run user sync (
POST /users/syncor scheduler).
A-Count calls:
GET https://api.anthropic.com/v1/compliance/organizationsGET https://api.anthropic.com/v1/compliance/organizations/{org_uuid}/users(paginated withnext_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
- Open cursor.com/dashboard (Cursor web dashboard).
- Go to the Settings tab.
- Open Advanced (or equivalent advanced / developer section, depending on UI labels).
- Find Admin API Keys (sometimes labeled alongside AI Code Tracking API for Enterprise).
- Click Create New API Key (or equivalent).
- Give the key a descriptive name (e.g.
A-Count inventory sync). - 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
- In A-Count Settings, open the Cursor provider (API mode).
- Paste the key into the provider API token field (stored encrypted like other providers).
- 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), thenGET /teams/members, so emails/names on spend rows can fill gaps when the members payload omits them. - Users:
GET /teams/members—teamMembers(also accepts nesteddataor amemberskey if present). Member id is resolved from several top-level and nested fields; if the API sends no id but sendsemail, we use a synthetic stable keyemail:<lowercased email>. - Fallback: If
teamMembersis empty but spend returns rows, users are built from spend only (same billing cycle fields inraw). - Optional enrichment: If spend fails, members are still returned when the members endpoint supplies data.
- Last activity (best-effort):
GET /teams/groups— firstjoinedAtseen per email (current + former members, plusunassignedGroup) is stored as activation time (activation_joined_at_msinraw).POST /teams/filtered-usage-events— paginated team-wide stream (noemailfilter; defaultpageSize1000,CURSOR_USAGE_EVENTS_PAGE_SIZE) overCURSOR_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). OptionalCURSOR_USAGE_EVENTS_INTER_PAGE_SLEEP_SEC. SetCURSOR_USAGE_EVENTS_DISABLE=1to skip this endpoint entirely (sync is much faster;last_activity_atthen uses onlyjoinedAtfrom groups). 429 retries:Retry-Afterwhen present, else exponential backoff starting at 1.25 s (capped).ProviderAccount.last_activity_atuses the latest usage timestamp when present; otherwise the activation (joinedAt) timestamp. If both calls fail or return nothing for a user,last_activity_atstays unset. Failures on groups or usage do not block the member import.
- Licenses inventory: not available from the API;
/licenses/sync/{id}returnslicenses_upserted: 0. License model (v2): P3 catalog-only — single packageref=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
isRemovedis true → alwaysuser(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_apicursor.member:id(string, as returned by Cursor: numeric or opaque e.g.user_…),is_removed,team_rolecursor.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_eventswhenlast_activity_atcomes from filtered usage;team_joinedwhen it falls back tojoinedAtfrom groups; omitted when neither is availablecursor.lastActivity: ISO UTC (…Z) for the effective timestamp written toProviderAccount.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)
- Open the Dropbox App Console and click Create app.
- Choose Scoped access (recommended for modern tokens).
- Choose Full Dropbox or the access type your security team allows; the critical part is that the token can use Team endpoints.
- 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). - 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
- Open Settings.
- Under Provider Connections, click Configure for Dropbox.
- Select API import mode.
- Paste the access token into API Token.
- 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) untilhas_moreis false. - Fields mapped: Account id, email, name, admin/owner flags, suspended flag, join date, profile photo URL (see
DropboxUser.from_dictinbackend/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→APImode →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
- In the
APIsection, locate the API Key. - If you need to generate a new one, create it and give it a clear name (e.g.
a-count-sync). - 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
- Open
Settings. - In
Provider Connections, clickConfigureforDropbox Sign. - Select
Import mode = API. - Paste the key into
API Token. - Click
Test. - 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_atis not available through the currently used endpoints and will remainnull- 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_reffrom 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_refvalues
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->APImode -> 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)
- Sign in as Superuser to ESET Business Account or ESET PROTECT Hub.
- Create a dedicated user. Superuser accounts cannot use the API.
- Enable Integrations. Grant read access to ESET PROTECT (and Inspect if licensed).
- The new user must complete the invitation email and log in once.
- In A-Count, choose Cloud, the region of the PROTECT/Inspect tenant, the API username, and the password.
3.2 On-prem PROTECT
- Enable the REST API in More -> Settings. Open API ports on the firewall.
- Assign a dedicated API user the API permission set. Administrator and 2FA users cannot use the API.
- Expose
https://<protect-host>:9443to the A-Count API host (or allow self-signed TLS if needed). - In A-Count, choose On-prem PROTECT, paste the REST URL, username, and password.
3.3 On-prem Inspect
- Use a dedicated Inspect API user (not the Administrator account).
- 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/installersfor product display name, subscription support code, and pool/site label. InstallerexpireTimeis 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_purchasedis managed devices with that product.assignedis devices that checked in within 30 days (ESET Hub-style active units).pricing_modelisper_license. - Expiration date: product
validityDateis stored as the manual renew date when ESET sends it onactiveProducts. This tenant often returns an emptyactiveProductslist, 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
- Open
Settings. - In
Provider Connections, clickConfigureon ESET. - Select
Import mode = API. - Choose Cloud, On-prem PROTECT, or On-prem Inspect.
- Enter region (cloud) or REST URL (on-prem), API username, and password.
- For on-prem self-signed certificates, uncheck SSL verification.
- Click
Test. - Save.
- 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
esetis seeded fromAPIProviderName. - Verify
esetis inPROVIDER_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 (
readusers) 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
- 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).
- 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
- Set
- Run user sync (
POST /users/syncor scheduler) — license inventory aggregates seat types first.
A-Count calls:
GET https://www.figma.com/scim/v2/{tenant_id}/Users?startIndex=&count=withAuthorization: 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 ismy-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
- Open Registering a GitHub App (GitHub Developer settings → GitHub Apps → New GitHub App).
- Name it clearly, for example
a-count. Webhook and Setup URL can stay empty for this provider. - Generate a private key and download the PEM. See Managing private keys for GitHub Apps.
- 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)
- Open
Settingsin A-Count. - Under
Provider Connections, clickConfigurefor GitHub. - Fill in
GitHub organization(slug). - Fill in:
GitHub App IDPrivate key (PEM)
- (Optional) Configure identity scan options (commit-scan limits and preferred domain).
- Save GitHub App setup.
- Use
Fetch org planto 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_disabledmembers list for the authenticated identity, A-Count inferstfa_enabled/tfa_level. Otherwise 2FA stays unknown. - Data Collected: login, name, email (if public/available), org role, plus a best-effort
last_activity_atderived 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 inbackend/.env.example. - License Tracking: A-Count reads organization metadata (
GET /orgs/{org}) to infer a plan snapshot and persists it underapi_config.github_license(the plan only). WhenGET /orgs/{org}/copilot/billingsucceeds, that snapshot is stored underapi_config.github_copilotand inventory creates a second license on the same organization (refcopilot_businessorcopilot_enterprise, seats =seat_breakdown.total, including 0).GET /orgs/{org}/copilot/billing/seatslinks that license through the shared add-onlicense_usageswriter, on one account per login (not one row per team). A failed billing read keeps the storedgithub_copilotsnapshot, including a one-time copy of a legacygithub_license.copilotobject. Copilot last activity is stored on thelicense_usagesrow (last_activity_at,activity_editor) and does not replace GitHublast_activity_at. Account lists return it inlicense_activitiesfor that license, including the time. The Copilot license column shows that seat date. The organization-plan column shows GitHublast_activity_atonly (commits and events). Profileupdated_atand repositorypushed_atare not last activity. The two values are not copied onto each other. Itsrawincludestags: ["ia"]andpricing_modelper_usage(one usage amount on the package, not a price times seats). Each license refresh setscostto the current calendar month's CopilotnetAmountandcurrencyto 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 stampsraw.license_reffrom 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_activityfails 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 Tokenfield 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.readonlyhttps://www.googleapis.com/auth/apps.licensinghttps://www.googleapis.com/auth/admin.directory.user.securityhttps://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
- Go to Google Cloud Console.
- Go to Google API Library and enable Admin SDK API and Enterprise License Manager API (see §2).
- Go to Credentials and create an OAuth client.
- Download the credentials JSON file.
- 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 (installedorwebsection).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_REDIRECTenv vars are ignored; useOIDC_REDIRECT_BASEandFRONTEND_ORIGINonly. MASTER_KEY: required to encrypt and decrypt the stored OAuth client JSON and authorized-user token.
5. Install In A-Count (recommended: Settings UI)
- Open Settings as an admin.
- Under Provider Connections, click Configure for Google.
- Choose API synchronization.
- 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).
- After consent, the API redirects to
{FRONTEND_ORIGIN}/settings?google_oauth=success(toast in Settings). - 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/callbackexchanges thecodeand stores the authorized-user token encrypted in the database. - Runtime auth:
_ensure_credentialsindata_providers/google.pyloads the encrypted token from the Google provider row, requires a refresh_token, and writes refreshed tokens back to the database. There is noInstalledAppFlow.run_local_serverfallback (unsuitable for Docker/production). - Directory API lists Workspace users; Licensing API lists license assignments.
customerIdcan be inferred from Directory API;GOOGLE_CUSTOMER_ID(or providerapi_config.google_customer_id) overrides when needed.- License inventory
contract_ref(P2 scope) = WorkspacecustomerId(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_tokenused 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 legacyGOOGLE_OAUTH_REDIRECT_URIfrom.env— it is ignored. - OAuth flow does not start: upload the client JSON from Settings and ensure
MASTER_KEYis 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.licensingscope is granted and that Enterprise License Manager API is enabled. - Missing users: verify that the
admin.directory.user.readonlyscope is granted. - Licensing API error about customer ID: set
GOOGLE_CUSTOMER_IDexplicitly 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 WorkspacecustomerId(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→APImode → upload service account JSON - Separate from Google (Workspace): GCP does not use the Workspace OAuth connection
- Security: the JSON key is encrypted with
MASTER_KEYand stored on thegcpprovider 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 P2contract_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)
- Open Google Cloud Console.
- 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
- Go to IAM & Admin → Service Accounts.
- Click Create service account.
- Name it clearly (e.g.
a-count-gcp-readonly). - Skip optional user access unless your org requires it.
- Open the new service account → Keys → Add key → Create new key → JSON.
- 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.
3.5 Find Your Organization ID (Optional But Recommended)
- In Cloud Console, open the resource picker → select your organization.
- Note the numeric Organization ID.
- In A-Count, paste either
123456789012ororganizations/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
- Open Settings.
- Under Provider Connections, find Google Cloud (not Google) → Configure.
- Select Import mode = API.
- Upload the service account JSON.
- Optionally set Organization ID (strongly recommended — limits project listing to your org).
- Click Test (lists reachable active projects).
- 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
getIamPolicyfallback 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.securityRevieweror 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/editorto 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
- Sign in as a Grammarly org admin → Admin panel.
- Open Organization → Configurations → OAuth 2.0 credentials.
- Add credential → enable License Management API (
users-api:readis enough for sync;users-api:writeis not used by A-Count yet). - Copy Client ID and Client Secret.
- In A-Count: set
import_mode = API, pasteclient_id:client_secret, click Test, then save. - 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, scopeusers-api:read)GET https://api.grammarly.com/ecosystem/api/v1/usersGET https://api.grammarly.com/ecosystem/api/v1/inviteesGET 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:
- Full header value — paste exactly what you would put after
Authorization:, for example:Basic <base64(serviceUserId:token)> - Base64 payload only — paste only
<base64(serviceUserId:token)>(without the wordBasic); A-Count will prependBasicautomatically.
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
- Open Settings.
- Under Provider Connections, click Configure for HiBob.
- Select API import mode.
- Paste the API Token value as described in section 2.
- Click Test, then Save.
4. What A-Count does
- Single request:
POST https://api.hibob.com/v1/people/searchwith JSON body:showInactive: truehumanReadable: "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:tokenpair with aBasicprefix, or an otherwise malformed Authorization value. - Fix: paste either the raw pair without
Basic(A-Count encodes it), or paste only the Base64 payload / fullBasic <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
showInactivebehavior 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
- Log in to HubSpot and open Settings (gear icon).
- In the left sidebar, navigate to Integrations → Private Apps.
- Click Create a private app.
- Give it a name (e.g.
A-Count Integration) and an optional description. - Go to the Scopes tab.
- Under CRM, find and enable
settings.users.read. - Click Create app and confirm.
- Copy the access token — HubSpot only shows the full token once. Store it securely.
3. Configure in A-Count
- Open Settings → Provider Connections.
- Find HubSpot and click Configure.
- Select API Sync mode.
- Paste the access token into the API Token field.
- Click Test to verify the token works.
- Click Save.
Once configured, HubSpot users will be synced automatically during scheduled syncs.
4. What Gets Synced
| Field | Source |
|---|---|
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:
- In HubSpot, go to Settings → Users & Teams and export the user list.
- Ensure each row has at least an email column.
- 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_atis 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→APImode →API Tokenasapi_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)
- In JetBrains Central Console, click your organization name (bottom-left) → Org administration.
- Open the API section.
- Click Enable API (org admin required).
3. Generate a personal API token
- Still under Org administration → API, click Generate Token.
- 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
- Open Settings in A-Count.
- Under Provider connections, click Configure for JetBrains.
- Select Import mode = API.
- Paste into API Token using this exact format:
api_key:customer_code- (
api_key|customer_codeis also accepted)
- 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_codewith 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 fieldsGET /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
- SentinelOne agent
serialNumber→ JumpCloud system (preferred). - Fallback: agent
computerName/ hostname → JumpCloudhostname/displayName. - User email from
primarySystemUser, or from the sole bound user on the system. - 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.IDPand import mode isapi, user sync runssync_jumpcloud_idp_groups(catalog +idp_group_members) andsync_idp_ou_department_catalogfromdepartmenton 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 stampsscope_id/scope_labelfromGET /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_labelempty. 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(orAdmin→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→APImode → fillBase 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 Keystab - Or:
Admin→Platform→API→ create a new API3 key
3.2 Create The Credential
- In Looker admin, navigate to or create a dedicated service account user (e.g.
a-count-sync@yourcompany.com). - Open the user's
API Keystab and clickNew API3 Key. - Copy the
Client IDandClient 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/syncreturns 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
- Open
Settings. - In
Provider Connections, clickConfigureon the Looker provider. - Select
Import mode = API. - Fill in:
- Base URL:
https://yourcompany.looker.com(no trailing slash) - Client ID: the API3 key ID
- Client Secret: the API3 key secret
- Base URL:
- Click
Test— a successful test authenticates and fetches 1 user. - 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.permissionsmay be empty in/users/{id}/rolesresponses. License deduction falls back to role name pattern matching (less precise). - Session endpoint availability:
/users/{id}/sessionsmay 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_emailorcredentials_googleblock. Theirlast_activity_atrelies on sessions orlast_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_urlhas no trailing slash and useshttps://. - Verify the
client_id/client_secretbelong 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_rolesto the service account and re-sync. - If
see_rolesis 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, orSettings→Provider Connections→Configure→CSVmode.
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
- Sign in at my.maxon.net with your Teams Admin account.
- Open Teams from the account menu.
- Go to the Users tab to view team members (name, email, groups, license assignments).
- For license usage details, use Licenses → License Usage (shows email, license name, last online check-in).
- 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
- Open Sync.
- Find Maxon in the provider list.
- Click Sync and upload your CSV file.
- Review task history for row counts (valid / invalid).
- 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_typeto capture product names when seat assignment is unclear per user. last_activity_atis 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.
- Open the Microsoft Entra admin center (sign in as Global Admin or Application Administrator).
- Go to Entra ID → App registrations (direct link: App registrations).
- Click New registration.
- Name: e.g.
A-Count. - Supported account types: Accounts in this organizational directory only (single tenant).
- Leave Redirect URI empty (A-Count uses client credentials, not interactive login).
- 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:
- Certificates & secrets → New client secret.
- Add a description and expiry → Add.
- 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
Settings→Provider Connections→ Microsoft →Configure.Import mode = API.- API Token:
tenant_id:client_id:client_secret. - Optional
api_configJSON:
{
"email_normalization_domain": "example.com",
"discovery": {
"sso_signin_lookback_days": 90,
"defender_enabled": true,
"defender_api_base": "https://api.security.microsoft.com"
}
}
- 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/userswith optionalsignInActivity, includingdepartmentwhen present on the directory object- Best sign-in timestamp: max of
lastSuccessfulSignInDateTime,lastSignInDateTime,lastNonInteractiveSignInDateTime - If
signInActivityis unavailable, computeslast_activity_atfrom Graph Reports (period D180 by default) and stores the source key inraw.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}/memberOfstores Azure AD group display names inraw.groups[]as{id, name}.departmentis 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/managedDevicesGET /deviceManagement/detectedApps+ per-app device linkage
EDR discovery (MicrosoftDefenderEdrSource)
GET https://api.security.microsoft.com/api/machinesGET /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
- Create the Entra app (§2); add Graph (+ WindowsDefenderATP if EDR); Grant admin consent.
- Configure Microsoft in A-Count (§4); Test then Save.
- Enable Microsoft as Identity provider (and MDM / EDR if needed).
- Run full Microsoft Sync (licenses then users).
- Run discovery: IdP → MDM → EDR (or full run).
- Authorize discovered apps to move them
in_scope. - Optionally disable JumpCloud/SentinelOne discovery roles if Microsoft covers the tenant.
9. Graph endpoints reference
POST /oauth2/v2.0/token(Graph scopehttps://graph.microsoft.com/.default; Defender scopehttps://api.securitycenter.microsoft.com/.default)GET /v1.0/users,/subscribedSkus,/auditLogs/signInsGET /v1.0/users/{id}/licenseDetails,/users/{id}/memberOfGET /v1.0/servicePrincipals(optional; requiresApplication.Read.All; probed before fetch)GET /v1.0/reports/*UserDetail(period='D180')(fallback activity mode)GET /deviceManagement/managedDevices,/deviceManagement/detectedAppsGET 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
- Go to Miro Your Apps.
- Click Create new app.
- Name the app (example:
A-Count-Sync) and confirm the correct team/organization context. - 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
- Open Install app and get OAuth token.
- Click Install app (or Reinstall if the app was already installed).
- Approve the permissions in Miro.
- Copy the Access Token shown after installation.
5. Install In A-Count
- Open Settings in A-Count.
- Under Provider connections, click Configure for Miro.
- Select API mode.
- Paste the token into API token.
- 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
licenseslug; catalogref= 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 P2contract_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 (notablyorganizations: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→APImode → 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
- Open Mixpanel → Organization Settings → Service Accounts.
- Create a service account with a descriptive name (e.g.
A-Count sync). - Copy the username and secret (secret is shown once).
3.2 Optional: SCIM token (Enterprise)
- Organization Settings → Security → SCIM.
- Generate a SCIM provisioning token.
- 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_idmay later be a P2contract_ref. - Default license on user rows:
mixpanel(license_ref=mixpanel)
7. Installation In A-Count
- Open Settings.
- Find Mixpanel → Configure.
- Set Import mode = API.
- Paste
username:secret→ Test (validates/me; also probes SCIM if env is set). - Run Sync (Users) from the Sync page or scheduler.
7. Limitations
last_activity_at: not available from/meor standard SCIM- Large
/mepayloads: sync usesunified_member_listonly (notmember_list); read timeout up to 120s - Roles: no per-member app role in
unified_member_list; A-Count storesentity_typeinraw - 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 |
9.7 Recommended implementation order (gaps)
- P0 — Already done: parse
workspacesas dict or list (data_providers/mixpanel.py_iter_workspace_dicts). - P1 — Role semantics: map
entity_type→employee_typeorrole(service_accountvsuser); optionallymembership_typeinschemas/mixpanel.py+MixpanelUserConverter. - P1 — Context in UI: promote
team_name,org_name,workspacename/id intoraw.mixpanel(not only nestedmember) inraw_payload. - P2 —
member_listfallback: only ifunified_member_listempty and product accepts payload size —mixpanel.pyget_users. - P3 —
last_activity_at: requires Mixpanel API that exposes per-user last activity (not in current/meor 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)
- In your monday.com account, click your profile picture.
- Select Developers (opens the Developer Center).
- Click API token → Show.
- Copy your personal token.
Option B: Administration tab (account admins only)
- In your monday.com account, click your profile picture.
- Select Administration → Connections → Personal API token.
- 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_productsappears in introspection but queries fail). - A-Count therefore binds:
- Paid / member seats from
account.tier(+ allaccount.productsas apps). - Guest / viewer seat classes from user
kind(or legacyis_guest/is_view_only).
- Paid / member seats from
- Org seat capacity uses
apps_monetization_info.seats_countandactive_members_countwhen available.
4. Install In A-Count
- Open Settings in A-Count.
- Under Provider connections, click Configure for Monday.
- Select API mode.
- Paste the token into API token.
- Click Test.
- 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.
- Admin → Security → SCIM → generate bearer token.
- Set server env:
MONDAY_SCIM_TOKEN=<scim bearer>MONDAY_SCIM_DOMAIN=<account-slug>(optional if GraphQL returnsaccount.slug, e.g.superplayforsuperplay.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; catalogref= slugified tier (e.g.pro). User sync stampslicense_refand 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_VERSIONis2026-01or later forproducts.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.tierand that the account has multi-productproductsreturned.
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
employeewheregiveaccess = '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
- Setup → Company → Enable Features → SuiteCloud
- REST Web Services
- Token-Based Authentication
- SuiteQL
- Setup → Integration → Manage Integrations — create integration; note Consumer Key / Consumer Secret.
- Setup → Users/Roles → Access Tokens — create token for a role with:
- SuiteQL permission
- Employee list/record read (View minimum)
- Copy Account ID (Setup → Company → Company Information).
2. Configure A-Count
- Set import mode to API.
- Open NetSuite → Configure and enter the five TBA fields separately:
- Account ID
- Consumer Key / Client ID
- Consumer Secret / Client Secret
- Token ID
- Token Secret
- Click Test connection — runs a lightweight SuiteQL probe on
employeewithgiveaccess = 'T'. - 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:
- Compliance API and audit events
- Endpoint:
GET https://api.chatgpt.com/v1/compliance/workspaces/{workspace_id}/users
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
- Open platform.openai.com/settings/organization/general.
- Confirm the Organization ID matches ChatGPT admin (wrong org = wrong key).
- Go to API keys → Create new secret key.
- Prefer Service account, name it clearly (e.g.
a-count-compliance), project Default, permissions All (OpenAI will replace these with Compliance scopes later). - 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
- Set
import_mode = API. - Paste the key as API Token.
- Set
api_config.openai_workspace_id=403b3d43-c36c-430a-9c6c-c4a3f83e3c98. - Test token → run user sync.
A-Count calls:
GET https://api.chatgpt.com/v1/compliance/workspaces/{workspace_id}/users(paginated withafter/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:
- Teams API
- API keys
- SCIM Provisioning (IdP → Parsec push only)
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
- Confirm the team is on Enterprise (pricing — “Access Parsec's Teams API…” is listed under Enterprise only).
- Sign in as a Super Admin.
- In the Teams admin portal → Keys → create a Public API Key (not a Computer Key /
tmkey_…). - Grant at least View Team Members.
- Copy the Team ID (dashboard sidebar / URL, or the
tapi_<teamId>.…key prefix). - In A-Count Settings → Parsec → API: paste Team ID + API key → Test → Save.
- 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 / roleis_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->APImode -> 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 Userswhen available in your tenant.
3.2 Create The Credential
- Create or select a dedicated service user.
- Give the service user the minimum scope that can view users.
- Generate an API token.
- Copy the token immediately; SentinelOne may show it only once.
- 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.
3.4 Reinstall / Consent Refresh
- 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:
GET /web/api/v2.1/agents— device metadata andlastLoggedInUserName/osUsername(the interactive user on the endpoint, not the management-console user).GET /web/api/v2.1/installed-applications— installed application inventory per agent (agentUuid, name, publisher, version,riskLevel).
How attribution works
- 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). - 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 syncedUser/ProviderAccountrecords. - Devices whose user cannot be resolved (including JumpCloud systems with multiple bound users and no primary user) are skipped (logged, not stored).
- 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/systemsandGET /api/v2/systems/{id}/usersfor 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 asEndpoint Security - Completeis stored as provider metadata. - License model (v2): catalog
ref=plan_key/ bundle name (lowercased). Inventory fetch usesrun_provider_callfor async sites/accounts APIs. User sync is assign-only whenlicense_type/plan_keyis present. - Expiration date:
/sites[].expirationor fallback/accounts[].expirationis 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/accountsfallback.
7. Installation In A-Count
- Open
Settings. - In
Provider Connections, clickConfigureon SentinelOne. - Select
Import mode = API. - Enter the SentinelOne URL, for example
https://euce1-108.sentinelone.net. - Paste the API token in
API Token. - Click
Test. - Save.
8. Known Limitations
last_activity_atdepends on SentinelOne returninglastLogin; otherwise A-Count usesupdatedAtorcreatedAtas a weak proxy.- User listing uses the token's own SentinelOne scope directly through
/users. - License inventory is site/account-agent-based (
activeLicensesoractiveAgents,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
ApiTokenprefix. - 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
sentineloneis present inUSERS_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 TokenintoAPI 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
- Go to api.slack.com/apps.
- Click the name of your application.
- Make sure you are editing the app connected to the correct workspace.
3. Add The Required Permissions
- In the left sidebar, under
Features, clickOAuth & Permissions. - Scroll down to the
Scopessection. - In
Bot Token Scopes, clickAdd an OAuth Scope. - Add these scopes:
users:readusers:read.emailteam.billing:readteam:read
- 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.
- Stay on the
OAuth & Permissionspage. - Go back to the top of the page.
- Click
Reinstall to Workspace. - Slack will show a confirmation screen with the updated permissions.
- Click
Allow.
5. Copy The API Token
- After the reinstall, return to
OAuth & Permissions. - Copy the
Bot User OAuth Token. - This is the token you must use in A-Count.
6. Install In A-Count
- Open
Settings. - In
Provider Connections, clickConfigurefor Slack. - Select
APImode. - Paste the token into
API Token. - Click
Test. - 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 clickReinstall 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 SCIMGET /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
- In Unity Dashboard, switch to the target organization.
- Go to Administration → Service accounts and create a service account.
- Assign the organization role SCIM Authenticator.
- Create credentials:
- Bearer (recommended): Bearer tokens → Add bearer token → copy the token
- Basic: Keys → Create key → copy key ID and secret
- Go to Administration → Single sign on.
- In the SCIM section, copy the SCIM base connector URL.
- 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) orbasicunity_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
- Bearer mode: long-lived bearer token in encrypted
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:
- Confirm SSO is configured and at least one domain is validated.
- Confirm the service account still has SCIM Authenticator.
- 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) withread: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
- 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)
- API Endpoint URL (example:
- Open Settings → Access Management → Service Accounts → Add Service Account (type: Custom Integration / GraphQL API).
- 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 returnstotalCount: 0even withread:userschecked, 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.
- Projects: leave empty (tenant-wide / all projects).
If the form shows
- Copy Client ID and Client Secret (secret is shown once).
- 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
- Set
- Run user sync (
POST /users/syncor scheduler) — license inventory runs first whenread:licensesis present.
A-Count calls:
POST https://auth.app.wiz.io/oauth/token(orauth.gov.wiz.iofor gov hosts) withgrant_type=client_credentialsandaudience=wiz-apiPOST {wiz_graphql_url}GraphQLusers(first, after)with cursor paginationPOST {wiz_graphql_url}GraphQLtenantLicensesfor 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.
- Open the Zoom App Marketplace (sign in as account admin).
- Go to Develop → Build App, or open Create app directly.
- Choose Server-to-Server OAuth (not OAuth, not JWT / legacy).
- Name: e.g.
A-Count. - Complete basic information if prompted (company name, developer contact email).
- Open the Scopes tab and add the permissions in §3.
- 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:
- Left sidebar → User → search
list_users→ checkuser:read:list_users:admin. - Still under User → search
settings→ checkuser:read:settings:admin(not account lock settings). - Left sidebar → Billing (not Accounts) → search
plan_usage→ checkbilling:read:plan_usage:admin. - 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
- Open Settings → Provider Connections.
- Find Zoom → Configure.
- Select API Sync mode.
- Paste the Account ID.
- In the credentials field, enter
client_id:client_secret. - Click Test, then Save.
Once configured, Zoom users and license inventory sync during scheduled syncs.
5. What Gets Synced
Users
| Field | Source |
|---|---|
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
- Create the S2S app (§2).
- Add the 3 granular scopes (§3):
user:read:list_users:admin,user:read:settings:admin,billing:read:plan_usage:admin; Activate. - Copy Account ID + Client ID + Client Secret.
- Configure Zoom in A-Count (§4); Test then Save.
- Run (or wait for) scheduled user + license sync.
- Confirm users appear and license inventory shows
plan_*rows (Pro+ account).
7. Known Limitations
- Per-user
typemaps to portal names: Basic (1) and Workplace Pro (2); not full SKU/pricing breakdown. license_info_listis returned only for some accounts (multiple/duplicate subscription plans).- Add-on seats (Large Meeting, Webinar, …) are read from
GET /users/{id}/settingsduring 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 directoryhttps://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 ReadRepository: 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.AllLicense 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 |
6. Minimum vs recommended vs optional (pattern)
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)
- Map capabilities (§1) — what does A-Count need to read?
- Pick auth model (§2) and document exact scope/permission strings.
- Document provider admin role and plan/tier gates.
- Document A-Count storage (§3) and Settings UI fields.
- Fill endpoint table (§5); wire
PROVIDER_REGISTRY/USERS_PROVIDERSand optionallylicense_sync.py. - Add the slug to
APIProviderName(keep inCSVProviderNameonly if dual-mode CSV remains). - 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 |