> ## Documentation Index
> Fetch the complete documentation index at: https://www.c1.ai/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# ID correlation recipes

> Battle-tested patterns for RawId annotation and per-connector ID formats.

Each recipe includes the problem, solution code, and rationale.

## Per-connector ID formats

**Problem:** Different external systems use different ID formats. Knowing what value to use for `RawId` annotation is critical for correlation.

**Solution:** Use the external system's native stable identifier:

| Connector | ID source | Format | Example |
| - | - | - | - |
| **Okta** | `app.Id` / `group.Id` | 20-char alphanumeric | `0oa1xyz789abcdef0h7` |
| **Azure AD** | Object ID (not App ID) | UUID | `12345678-1234-1234-1234-123456789012` |
| **GCP** | Resource path | `projects/{id}` | `projects/my-project-123` |
| **AWS** | ARN | Full ARN | `arn:aws:iam::123456789012:role/Admin` |
| **GitHub** | Node ID or numeric ID | Integer as string | `12345678` |
| **Salesforce** | Salesforce ID | 18-char ID | `00e3h000000bRQAAA2` |
| **Google Workspace** | Google Group ID | Variable | `00gjdgxs3x1h123` |
| **Databricks** | Connector resource ID (varies by resource type) | See [Databricks resource IDs](#databricks-resource-ids) | `account/8c6f99ec-78ce-4654-8f92-e716b3dd67a7/group/79416186968854` |

**Why this matters:** C1 uses these IDs to correlate resources across syncs. Using the wrong ID causes duplicate objects or failed correlations.

**Key points:**

* Azure AD has two IDs: Object ID (use this) and Application ID (client ID for OAuth)
* AWS uses full ARNs, not account IDs alone
* GitHub has numeric IDs and GraphQL node IDs; either works but be consistent
* Databricks does not use the native ID for every resource type. Groups, workspaces, and workspace roles use a connector-built value, so check the table below before setting `match_baton_id`

### Databricks resource IDs

baton-databricks (v0.1.16 and later) sets `RawId` to the connector's resource ID. For groups, that is a compound path, not the Databricks group ID. A bare group ID in `match_baton_id` does not merge: after sync, the connector's group appears as a second resource next to the pre-created one.

| Resource type | `match_baton_id` value | Example |
| - | - | - |
| Group (account) | `account/<account-id>/group/<group-id>` | `account/8c6f99ec-78ce-4654-8f92-e716b3dd67a7/group/79416186968854` |
| Group (workspace) | `workspace/<deployment-name>/group/<group-id>` | `workspace/dbc-dd4d071e-0b85/group/79416186968854` |
| Workspace | Deployment name, not the numeric workspace ID | `dbc-dd4d071e-0b85` |
| Workspace role | `<deployment-name>:<role>` | `dbc-dd4d071e-0b85:databricks-sql-access` |
| Account role | Role name | `account_admin` |
| User | Databricks user ID | `74984374645487` |
| Service principal | Databricks service principal ID | `71985169525637` |

Entitlements match on the parent resource's `match_baton_id` plus the slug (for example, `member` on a group).

## Setting RawId annotation

**Problem:** Ensure C1 can correlate your resources across syncs and match resources created via Terraform.

**What is RawId?** A string annotation containing the external system's native identifier. C1 uses this to match resources across syncs and to merge Terraform-created objects with connector-discovered ones.

**Solution:** Add the `RawId` annotation when building resources:

```go theme={"theme":{"light":"css-variables","dark":"css-variables"}}
// Inline with resource creation (preferred)
userResource, err := resource.NewUserResource(
    user.DisplayName,
    userResourceType,
    user.ID,
    userTraitOptions,
    resource.WithAnnotation(&v2.RawId{Id: user.ID}),
)

// Or add after creation
groupResource, err := resource.NewGroupResource(
    group.Name,
    groupResourceType,
    group.ID,
    groupTraitOptions,
)
groupResource.WithAnnotation(&v2.RawId{Id: group.Email})
```

**What value to use:** The external system's native, stable identifier. Choose an ID that:

* Won't change when the resource is renamed or modified
* Is unique within that resource type
* Is what an admin would recognize from the external system

## Examples from production connectors

| System | Resource | RawId value | Why |
| - | - | - | - |
| Okta | User | `user.Id` (`00u1abc...`) | Okta's internal ID, stable across renames |
| Okta | App | `app.Id` (`0oa1xyz...`) | Okta's internal app ID |
| Google Workspace | User | `user.PrimaryEmail` | Email is the canonical identifier |
| Google Workspace | Group | `group.Email` | Group email is stable and recognizable |
| GCP | Project | `project.ProjectId` | Project ID (not display name) |
| AWS | Role | Full ARN | ARNs are globally unique |
| Azure AD | User | Object ID (UUID) | Not the UPN, which can change |

## Common mistakes

| Mistake | Problem | Fix |
| - | - | - |
| Using display names | Change when renamed | Use system-generated IDs |
| Using array indices | Change with ordering | Use stable identifiers |
| Different IDs for same resource | Correlation fails | Use consistent ID source |
| Using mutable fields | Resource appears as new each sync | Use immutable system IDs |

## When to use RawId

**Should have RawId:**

* Apps, groups, roles, and any resource that might be pre-created via Terraform
* Resources that need stable correlation across syncs
* Any resource type where admins might reference objects by external ID

**May not need RawId:**

* Ephemeral or derived resources
* Resources only used internally by the connector
