Skip to main content
What happens when an employee leaves your company but still has access to your production database? Or your billing system? Or your customer data? A connector answers the question: who has access to what? ConductorOne needs to know about users, groups, roles, and permissions across all your systems. But every system stores this information differently. Okta has users and groups. AWS has IAM roles and policies. Salesforce has profiles and permission sets. The Baton connector framework solves this: you write one integration, and ConductorOne handles the rest. A connector bridges this gap. It translates access data from any system into a common format that ConductorOne understands. Once connected, you get unified visibility across your entire infrastructure. In Baton terms, a connector is a program that can:
  • List resources (users, groups, roles, apps, projects, etc.)
  • Define entitlements (the permissions you can grant on those resources)
  • Emit grants (the facts of who currently has which entitlements)

Where connectors fit

Your Systems                    ConductorOne
+------------------+            +------------------+
| Okta (IdP)       |  <------>  |                  |
| AWS              |  <------>  |  Unified Access  |
| Salesforce       |  Connector |  Management      |
| GitHub           |  <------>  |                  |
| Your Custom App  |            |                  |
+------------------+            +------------------+
      ^                                  |
      |     Reconciliation Loop          |
      +----------------------------------+
Most people interact with connectors in two ways:
  • Deploy an existing connector (you configure it; you do not write Go)
  • Build or extend a connector (you write Go against baton-sdk)

Sync and provision

Connectors do two things: Sync (read): Pull access data from your systems into ConductorOne
  • Who exists? What groups? What roles?
  • What permissions are available?
  • Who has what access right now?
Provision (write): Push access changes back to your systems
  • Grant: Give someone access they’ve been approved for
  • Revoke: Remove access that’s been terminated
  • Create Account: JIT (Just-In-Time) provisioning
  • Delete Resource: Remove accounts entirely
Together, sync and provision create a reconciliation loop: ConductorOne sees what access exists (sync), compares it to what access should exist (policy), and corrects any drift (provision). Your access controls become self-healing.

The special role of identity providers

Identity Providers (IdPs) like Okta, Azure AD, or Google Workspace have a unique position: they’re often the source of truth for who your users are. Most connectors sync users from the target system. But IdP connectors do more:
  • They define the canonical user identities
  • Other systems’ users are correlated back to IdP users
  • User lifecycle (join, move, leave) often originates in the IdP
When you connect an IdP, you’re establishing the identity foundation that other connectors build upon.

Understanding the tools

Before diving into implementation, it helps to understand which tools do what. The ConductorOne ecosystem has several SDKs and CLIs with different purposes.

For connector developers

ToolPurposeWhen to use
baton-sdkGo SDK for building connectorsBuilding or extending a connector
batonCLI for inspecting sync outputDebugging .c1z files locally
baton-sdk is the Go library you import when building a connector. It provides:
  • The ResourceSyncer interface you implement
  • Pagination helpers for API calls
  • Resource and grant builders
  • HTTP client utilities with retry logic
import "github.com/conductorone/baton-sdk/pkg/connectorbuilder"
baton is a standalone CLI for inspecting .c1z files. After your connector runs, use it to verify the output:
baton resources -f sync.c1z    # List synced resources
baton grants -f sync.c1z       # List grants
baton entitlements -f sync.c1z # List entitlements

For ConductorOne users (not connector development)

ToolPurposeWhen to use
coneCLI for ConductorOne platformAccess requests, approvals, searches
conductorone-sdk-goGo SDK for ConductorOne APIIntegrating with C1 platform
cone is the ConductorOne CLI for end-users and administrators. It handles access management workflows:
cone login                     # Authenticate to ConductorOne
cone search users "alice"      # Search for users
cone task approve <task-id>    # Approve access requests

Which tool do I need?

I want to…Use this
Build a new connectorbaton-sdk
Debug my connector’s outputbaton CLI
Request or approve accesscone CLI
Build an app that uses ConductorOneconductorone-sdk-go
Deploy a pre-built connectorThe connector binary
cone and baton are separate tools for separate purposes. You don’t use cone to build connectors, and you don’t use baton to manage access requests.

The connector binary

When you build a connector, you produce a standalone binary (e.g., baton-okta, baton-github). This binary:
  • Embeds the SDK - It’s compiled with baton-sdk, not dependent on it at runtime
  • Is self-contained - No runtime dependencies except the target system’s API
  • Runs independently - You don’t need any other ConductorOne tools installed
  • Produces standard output - A .c1z file that any ConductorOne environment can consume
# The connector IS the binary
./baton-okta --domain example.okta.com --api-token $TOKEN

# It produces a .c1z file
ls -la sync.c1z
The “connector” is NOT the SDK. It’s the compiled program that uses the SDK.

The minimum contract

The beauty of the Baton SDK is how little you need to implement. At the SDK level, the primary interface for connector developers is ResourceSyncer:
  • ResourceType(ctx): define the type (and traits) of a resource (for example “user” with trait TRAIT_USER)
  • List(ctx, parentResourceID, token): list instances of that resource type (paged)
  • Entitlements(ctx, resource, token): list entitlements offered by that resource (paged)
  • Grants(ctx, resource, token): list who has those entitlements (paged)
Four methods give you a working connector. The SDK handles pagination orchestration, error handling, and output formatting. You focus on translating your target system’s API into the common model. Optional extensions add provisioning and lifecycle operations when you need them.

When to build vs reuse

  • Use an existing connector when it exists and your needs are met. The operational cost is configuration and deployment.
  • Contribute upstream when the connector exists but is missing a capability you need (for example, adding a resource type or provisioning path). This reduces long-term fork burden and helps the community.
  • Build a new connector when the target system is unsupported or proprietary. With the SDK handling the heavy lifting, most connectors can be built in a few days.
One reality to keep in mind: connector capabilities vary across the ecosystem. If you need provisioning, check for it per connector/version. Consider alternatives when:
  • A pre-built connector exists and meets your needs
  • You need quick integration - use baton-http for REST APIs
  • You need database integration - use baton-sql for SQL databases

What “working” looks like

baton-demo is an example connector with hardcoded data that demonstrates a fully runnable sync. No API credentials needed - you can try it right now.
baton-demo
baton resources
Or via docker:
docker run --rm -v $(pwd):/out ghcr.io/conductorone/baton-demo:latest -f "/out/sync.c1z"
docker run --rm -v $(pwd):/out ghcr.io/conductorone/baton:latest -f "/out/sync.c1z" resources
Example output:
$ baton resources -f sync.c1z
Resource Type    Count
user             127
team             23
repository       89

$ baton grants -f sync.c1z | head -5
Principal        Entitlement           Resource
alice@corp.com   member                engineering-team
alice@corp.com   admin                 api-repo
bob@corp.com     member                platform-team
bob@corp.com     read                  docs-repo

What “working” does not guarantee

  • “Runs locally” does not mean “safe in production.” You still need to handle pagination, retries, and rate limits (and prove you do).
  • Provisioning is not implied by syncing. Many connectors are read-only or partially provisionable; you must check per connector.
  • Docs-site capability tables are not the API contract. They are a directory for humans; use the connector’s own manifests and the SDK interfaces as ground truth.
Connectors sync access data, not business data. Users, roles, permissions, API keys - yes. Customer records, issues, messages, logs - no.

Next steps

Prerequisites for building

If you’re going to build a connector, you’ll need:
ToolVersionPurpose
GoSee go.modConnector runtime
GitAnyVersion control
makeAnyBuild automation
Go version requirements vary by connector and SDK version. Check the go.mod file in baton-sdk or your target connector for the current minimum version.
Plus:
  • Access to the system you want to connect to
  • API credentials with read permissions
  • An IDE with Go support (VS Code, GoLand)

Quick reference

TermMeaning
ConnectorGo binary that syncs and provisions access data
c1zCompressed sync output file
ResourceUser, group, role, or custom entity
EntitlementPermission that can be granted
GrantAssignment of entitlement to principal
Baton SDKGo library that handles sync orchestration
SyncReading access data from a system
ProvisionWriting access changes back (grant, revoke, create, delete)
ReconciliationComparing actual vs desired access and correcting drift