Skip to main content

Loading a Workspace Account

A Workspace Account in Accord is exposed through three joined records: the WorkspaceAccount itself plus the Account and Profile it points at. This page explains those three layers and shows the one GraphQL call that recomposes them into a single object you can render.

The data model

TypeWhat it representsScope
AccountPlatform-level identity. One per real person across the entire system.Global
ProfilePersonal info: name, picture, job title, and contact details. Shared everywhere a person appears. Profile.id matches Account.id (one-to-one).Global
WorkspaceAccountMembership of an Account in one specific workspace. Carries the workspace-specific role, seat type, contact email, and invitation state. One per workspace the Account belongs to.Workspace

Almost every "render a Workspace Account" task in an Accord (in a list of members, on a comment, on a step assignment) starts from a WorkspaceAccount and traverses to the Account and Profile it points at.

Account (id = X)
│ 1:1

Profile (id = X)


WorkspaceAccount ─── (account_id ──┘
(workspace_id, account_id)

The query

Given a workspaceAccount.id, this single call returns everything you need to render the Workspace Account inside that workspace:

query LoadWorkspaceAccount($workspaceAccountId: String!) {
workspaceAccount(id: $workspaceAccountId) {
# Workspace-specific membership
id
workspaceId
role
seatType
contactEmail
acceptedAt
isPartner

workspace {
id
name
}

# Platform identity
account {
id
email
type
lastSignInAt
}

# Display info (shared across workspaces)
profile {
firstName
lastName
preferredFirstName
preferredLastName
picture
preferredPicture
jobTitle
linkedinUrl
timezone
phoneNumber
}
}
}

Variables

{ "workspaceAccountId": "wa_01H…" }

Response shape (abbreviated)

{
"data": {
"workspaceAccount": {
"id": "wa_01H…",
"workspaceId": "ws_01H…",
"role": "accord_solution_provider_role",
"seatType": "full",
"contactEmail": "[email protected]",
"acceptedAt": "2026-04-12T18:22:09.000Z",
"isPartner": false,
"workspace": { "id": "ws_01H…", "name": "Acme" },
"account": {
"id": "ac_01H…",
"email": "[email protected]",
"type": "USER",
"lastSignInAt": "2026-05-25T14:01:33.000Z"
},
"profile": {
"firstName": "Alexandra",
"lastName": "Chen",
"preferredFirstName": "Alex",
"preferredLastName": "Chen",
"picture": "https://…/avatar.png",
"preferredPicture": "https://…/preferred.png",
"jobTitle": "VP Sales",
"linkedinUrl": "https://www.linkedin.com/in/alex-chen/",
"timezone": "America/Los_Angeles",
"phoneNumber": null
}
}
}
}

Roles

workspaceAccount.role is the role a Workspace Account has within its workspace. Two values cover the vast majority of accounts you'll work with:

RoleWhoWhat
accord_solution_provider_roleYour sellers: the internal team running deals on Accord.Full access to the workspace's Accords, Playbooks, library, and admin surfaces.
accord_customer_roleYour buyers: external customer-side participants invited into an Accord.Sees only the Accords they've been added to, scoped to the customer-visible portion.

When you filter or branch on role, comparing against these two values is almost always what you want:

query SellersInWorkspace($workspaceId: String!) {
workspaceAccountsConnection(
filter: {
workspaceId: { equalTo: $workspaceId }
role: { equalTo: "accord_solution_provider_role" }
deletedAt: { isNull: true }
}
) {
edges {
node {
id
contactEmail
profile {
preferredFirstName
preferredLastName
}
}
}
}
}

Other role values exist for internal and system purposes but aren't relevant to most integrations.

Looking up by (workspaceId, accountId)

If you know which workspace and which account but not the workspaceAccount.id, use the unique-key lookup PostGraphile generates from the composite unique constraint:

query LoadWorkspaceAccountByAccount(
$workspaceId: String!
$accountId: String!
) {
workspaceAccountByWorkspaceIdAndAccountId(
workspaceId: $workspaceId
accountId: $accountId
) {
id
role
account { email }
profile { preferredFirstName preferredLastName picture }
}
}

Common gotchas

  • Display names: use preferred* first. Treat preferredFirstName / preferredLastName as the customer-facing display name, falling back to firstName / lastName when they're empty. Same pattern for preferredPicture vs picture.
  • Two emails. workspaceAccount.contactEmail is the workspace-specific email (used on shared Accords, notifications); account.email is the platform login. Often identical, sometimes intentionally different.
  • Placeholder rows. A profile.placeholder = true row is a stub: a stakeholder you've referenced who hasn't accepted an invite yet. Their account may still exist (so they can be invited), but acceptedAt on the workspaceAccount will be null.
  • Soft deletes. workspaceAccount, account, and profile all use deletedAt. Connections and filters exclude soft-deleted rows by default; if you start hand-rolling SQL or admin tools, remember they're still in the database.

Where to go next