How Account Linking Works Between Grok Web, Build, and Console in grok2api
Grok Web, Build, and Console accounts are linked through two many-to-one association tables—account_provider_links for Web-to-Build relationships and web_console_account_links for Web-to-Console relationships—enabling cross-provider operations with PostgreSQL advisory locks ensuring data consistency.
The grok2api repository implements a multi-provider identity architecture where each service maintains distinct account records. Understanding how account linking works between Grok Web, Build, and Console is essential for administrators managing unified user identities and performing cross-platform operations.
Data Model and Database Schema
Provider-Specific Account Records
Each service maintains its own provider account records identified by the provider fields grok_web, grok_build, and grok_console. Rather than merging these into a single table, the system preserves separate records for each platform while establishing formal relationships through dedicated link tables.
Association Tables
The relational persistence layer defines two critical junction tables in backend/internal/infra/persistence/relational/models.go:
account_provider_links: Establishes Web-to-Build relationships with columnsweb_account_idandbuild_account_id(lines 92-100)web_console_account_links: Establishes Web-to-Console relationships with columnsweb_account_idandconsole_account_id(lines 102-108)
Both tables enforce unique constraints preventing a Web account from linking to multiple Build or Console accounts, and vice versa. The database schema includes check constraints (chk_account_provider_links_distinct) that maintain one-to-one cardinality between linked providers.
The Account Linking Flow
The linking process follows a layered architecture ensuring transactional safety:
-
API Endpoint: The admin UI issues
POSTorPUTrequests to/api/admin/v1/accounts/:id/link, handled inbackend/internal/transport/http/account/handler.go -
Service Layer: The handler delegates to
AccountService.LinkWebToBuild(or the Console equivalent) for business logic validation -
Repository Implementation:
LinkWebToBuildinbackend/internal/infra/persistence/relational/account_repository.goexecutes the insert within a database transaction (lines 941-950) -
Concurrency Control: A PostgreSQL advisory lock (
accountLinkLock) serializes concurrent mutation attempts, preventing race conditions that could create duplicate links (tested inpostgres_integration_test.go) -
Referential Integrity: Foreign-key constraints with
ON DELETE CASCADEautomatically remove link table rows when the parent Web account is deleted, maintaining database consistency without manual cleanup
Retrieving Linked Accounts
When fetching account details via GET /api/admin/v1/accounts/:id, the system populates a linkedAccounts slice containing relationship metadata. The response structure defined in backend/internal/transport/http/account/handler.go (lines 310-313) includes:
linkedAccountId: The UUID of the paired accountlinkedProvider: The provider type (grok_buildorgrok_console)linkedAccounts: An array of objects containingid,provider,name,email, anduserId
This allows admin interfaces to display complete relationship graphs without requiring additional API calls.
Cascading Deletion of Linked Accounts
Account deletion supports optional cascading to linked peers through the linkedDeleteTargets parameter. When a request includes specific providers (e.g., ["grok_build","grok_console"]), the system:
- Parses targets using
parseLinkedDeleteTargetsinhandler.go(line 1312) - Resolves linked IDs via
ResolveLinkedDeleteIDsinbackend/internal/repository/account.go - Expands the deletion scope to include one-hop linked accounts
- Reports both
deleted(root) andlinkedDeleted(peer) counts in the response (lines 663-669)
This atomic operation ensures that removing a Grok Web account can simultaneously clean up associated Build and Console identities without orphaning records.
Implementation Examples
Linking a Web Account to a Build Account
The repository method handles the actual database insertion with transactional safety:
// ctx – request context
// webID – ID of the Grok Web account
// buildID – ID of the Grok Build account to link
err := accountsRepo.LinkWebToBuild(ctx, webID, buildID)
if err != nil {
// handle ErrConflict (already linked) or other DB errors
}
This corresponds to the implementation in backend/internal/infra/persistence/relational/account_repository.go where the function inserts a row into account_provider_links while respecting the advisory lock mechanism.
Querying Linked Accounts via HTTP
curl -H "Authorization: Bearer <admin-token>" \
https://api.grok.com/api/admin/v1/accounts/12345
The response includes relationship metadata:
{
"id": 12345,
"provider": "grok_web",
"linkedAccountId": 67890,
"linkedProvider": "grok_build",
"linkedAccounts": [
{
"id": 67890,
"provider": "grok_build",
"name": "my-build-acct",
"email": "build@example.com",
"userId": "build-user"
}
]
}
Deleting with Cascading Peers
POST /api/admin/v1/accounts/12345/delete
{
"provider": "grok_web",
"linkedDeleteTargets": ["grok_build"]
}
The handler processes the linkedDeleteTargets array to determine which associated providers should be removed alongside the primary account.
Summary
- Dual Table Architecture: Account linking between Grok Web, Build, and Console relies on separate
account_provider_linksandweb_console_account_linkstables to maintain strict relationship cardinality - Transactional Safety: All linking operations use PostgreSQL advisory locks and database transactions to prevent duplicate entries during concurrent access
- API Integration: The HTTP layer exposes linking capabilities through
/api/admin/v1/accounts/:id/linkwith comprehensive linked account metadata in GET responses - Cascading Operations: Deletion requests can propagate to linked accounts via
linkedDeleteTargets, with the service layer handling ID resolution and atomic cleanup - Source Locations: Core implementations reside in
backend/internal/infra/persistence/relational/account_repository.gofor data access andbackend/internal/transport/http/account/handler.gofor HTTP transport concerns
Frequently Asked Questions
What database tables store the account linking relationships?
The system uses two tables defined in backend/internal/infra/persistence/relational/models.go: account_provider_links connects Grok Web to Grok Build accounts, while web_console_account_links connects Grok Web to Grok Console accounts. Both tables use composite primary keys and unique constraints to enforce one-to-one relationships between providers.
How does grok2api prevent duplicate account links?
The implementation uses PostgreSQL advisory locks (accountLinkLock) to serialize concurrent linking attempts, combined with database-level unique constraints on the link tables. This ensures that two simultaneous requests cannot create conflicting associations between the same Web and Build (or Console) accounts.
Can I link one Grok Build account to multiple Grok Web accounts?
No. The database schema enforces unique constraints that prevent many-to-many relationships. Each Build account can link to only one Web account, and each Web account can link to only one Build account. This one-to-one cardinality is maintained through the chk_account_provider_links_distinct check constraint.
What happens to linked accounts when I delete a Grok Web account?
By default, only the Web account is deleted. However, if you specify linkedDeleteTargets in the deletion request (e.g., ["grok_build","grok_console"]), the system resolves the linked IDs and performs an atomic deletion of all specified accounts. Additionally, foreign-key constraints with ON DELETE CASCADE automatically clean up orphaned entries in the link tables regardless of the cascading deletion option.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →