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
| Type | What it represents | Scope |
|---|---|---|
Account | Platform-level identity. One per real person across the entire system. | Global |
Profile | Personal info: name, picture, job title, and contact details. Shared everywhere a person appears. Profile.id matches Account.id (one-to-one). | Global |
WorkspaceAccount | Membership 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",
"acceptedAt": "2026-04-12T18:22:09.000Z",
"isPartner": false,
"workspace": { "id": "ws_01H…", "name": "Acme" },
"account": {
"id": "ac_01H…",
"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:
| Role | Who | What |
|---|---|---|
accord_solution_provider_role | Your sellers: the internal team running deals on Accord. | Full access to the workspace's Accords, Playbooks, library, and admin surfaces. |
accord_customer_role | Your 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. TreatpreferredFirstName/preferredLastNameas the customer-facing display name, falling back tofirstName/lastNamewhen they're empty. Same pattern forpreferredPicturevspicture. - Two emails.
workspaceAccount.contactEmailis the workspace-specific email (used on shared Accords, notifications);account.emailis the platform login. Often identical, sometimes intentionally different. - Placeholder rows. A
profile.placeholder = truerow is a stub: a stakeholder you've referenced who hasn't accepted an invite yet. Theiraccountmay still exist (so they can be invited), butacceptedAton the workspaceAccount will benull. - Soft deletes.
workspaceAccount,account, andprofileall usedeletedAt. 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
- Loading an Accord: the same
WorkspaceAccountshows up insideaccord.accordMembers. - Filtering & conditions: listing accounts (
workspaceAccounts(filter: …)) for a workspace.