Accord Developer API (Guide)
Accord API Guide for common Summary update workflows – the same patterns can be repurposed for other parts of an Accord.
Table of Contents
Overview
This article acts as a detailed guide, explaining how to locate and modify Accord Summaries. For additional information on the Accord Developer API, please see the accompanying article, Accord Developer API (Overview).
1. Create an accord from a template (playbook)
In Accord, a template is just an Accord with isTemplate: true. To spin up a new accord from one, use the duplicateAccord mutation. It clones the template’s stages, steps, summaries, sections, and resources into a fresh accord.
Mutation
mutation CreateAccordFromTemplate(
$accordId: String!
$workspaceId: String
$ownerEmail: String
) {
duplicateAccord(
input: {
accordId: $accordId
workspaceId: $workspaceId
ownerEmail: $ownerEmail
style: "from-template"
}
) {
accordId
accord {
id
accountName
opportunityName
createdFromTemplateId
workspaceId
createdAt
}
}
}
Variables
| Variable | Type | Required | Description |
|---|---|---|---|
accordId |
String! |
Yes | The ID of the template accord (the one with isTemplate: true) you want to clone. |
workspaceId |
String |
No | Workspace to create the new accord in. Defaults to the template’s workspace. |
ownerEmail |
String |
No | Email of the workspace user who should own the new accord. Defaults to the caller. |
Example variables
{
"accordId": "f1e2d3c4-b5a6-4789-0123-456789abcdef",
"ownerEmail": "ae@yourcompany.com"
}
Tip — finding template IDs
If you don’t have the template ID handy, list them with:
query ListTemplates {
accords(
filter: { isTemplate: { equalTo: true }, deletedAt: { isNull: true } }
orderBy: [TEMPLATE_NAME_ASC]
) {
id
templateName
workspaceId
}
}
2. Get an accord by ID
The simplest read in the API. Use the singular accord query when you already know the ID.
Query
query GetAccordById($id: String!) {
accord(id: $id) {
id
accountName
opportunityName
opportunityAmount
closeDate
crmId
crmType
pointPersonWorkspaceAccountId
pointPersonName
status
progress
createdFromTemplateId
workspaceId
createdAt
updatedAt
}
}
Variables
{ "id": "a1b2c3d4-e5f6-4789-0123-456789abcdef" }
If no accord matches the ID (or it has been deleted), accord returns null.
3. Find an existing accord by account, opportunity, or point person
The accords list query takes a rich filter argument. You can match any combination of accountName, crmId (the CRM opportunity / deal ID), and pointPersonWorkspaceAccountId. Combine clauses with and for AND semantics, or or for OR semantics.
Naming note: in the Accord schema,
crmIdis the ID of the linked CRM opportunity / deal (e.g. a Salesforce Opportunity ID or HubSpot Deal ID).accountNameis the customer company name.pointPersonWorkspaceAccountIdis the workspace user assigned as point person.
Query
query FindAccord(
$accountName: String
$crmId: String
$pointPersonWorkspaceAccountId: String
) {
accords(
filter: {
and: [
{ isTemplate: { equalTo: false } }
{ deletedAt: { isNull: true } }
{ accountName: { equalTo: $accountName } }
{ crmId: { equalTo: $crmId } }
{ pointPersonWorkspaceAccountId: { equalTo: $pointPersonWorkspaceAccountId } }
]
}
orderBy: [UPDATED_AT_DESC]
first: 20
) {
id
accountName
opportunityName
crmId
crmType
pointPersonWorkspaceAccountId
pointPersonName
status
updatedAt
}
}
How it behaves
equalTo: null(i.e. omitting the variable) matches only records whose value is alsonull. So if you want a clause to be optional, simply remove it from theandarray rather than passingnull.- To match any one of several values, swap
equalToforin: ["...", "..."]. accountNameis a case-sensitive exact match. For fuzzy matching useincludesInsensitiveinstead ofequalTo.
Examples
By account only:
{ "accountName": "Acme Corp" }
filter: {
and: [
{ isTemplate: { equalTo: false } }
{ deletedAt: { isNull: true } }
{ accountName: { equalTo: $accountName } }
]
}
By CRM opportunity / deal ID only (most reliable, since CRM IDs are unique):
filter: {
and: [
{ deletedAt: { isNull: true } }
{ crmId: { equalTo: $crmId } }
]
}
By point person + account:
filter: {
and: [
{ isTemplate: { equalTo: false } }
{ deletedAt: { isNull: true } }
{ accountName: { equalTo: $accountName } }
{ pointPersonWorkspaceAccountId: { equalTo: $pointPersonWorkspaceAccountId } }
]
}
4. Get summaries by accord ID
Each accord has zero or more Summary records (the tabs/pages of the customer-facing experience). Use the summaries list query, filtered by accordId.
Query
query GetSummariesByAccord($accordId: String!) {
summaries(
filter: {
accordId: { equalTo: $accordId }
deletedAt: { isNull: true }
}
orderBy: [ORDER_ASC]
) {
id
title
order
internal
bannerImageUrl
accordId
workspaceId
createdAt
updatedAt
}
}
Variables
{ "accordId": "a1b2c3d4-e5f6-4789-0123-456789abcdef" }
The result is an array of summaries ordered by their display order. Set internal: false in the filter if you only want customer-visible summaries.
5. Get sections for a summary
Each summary contains an ordered list of Section records (text blocks, picklists, mutual action plans, etc.). Filter the sections query by summaryId.
Query
query GetSectionsBySummary($summaryId: String!) {
sections(
filter: {
summaryId: { equalTo: $summaryId }
deletedAt: { isNull: true }
}
orderBy: [ORDER_ASC]
) {
id
title
type
order
data
internal
summaryId
workspaceId
createdAt
updatedAt
}
}
Variables
{ "summaryId": "11111111-2222-3333-4444-555555555555" }
The data field is a JSON blob whose shape depends on type (text editor content, picklist selections, etc.). It’s returned as-is — your client should branch on type when interpreting it.
Bonus — fetch summaries and their sections in one round trip
Because the schema exposes nested relationships, you can grab everything for an accord in a single call:
query AccordWithSummariesAndSections($accordId: String!) {
accord(id: $accordId) {
id
accountName
summaries(
filter: { deletedAt: { isNull: true } }
orderBy: [ORDER_ASC]
) {
id
title
order
sections(
filter: { deletedAt: { isNull: true } }
orderBy: [ORDER_ASC]
) {
id
title
type
order
data
}
}
}
}
6. Get a section for a summary matching a given title
Same query as above, with an extra title clause. Section titles are not guaranteed to be unique within a summary, so this returns a list — usually you’ll take the first result.
Query
query GetSectionByTitle($summaryId: String!, $title: String!) {
sections(
filter: {
summaryId: { equalTo: $summaryId }
title: { equalTo: $title }
deletedAt: { isNull: true }
}
orderBy: [ORDER_ASC]
first: 1
) {
id
title
type
order
data
}
}
Variables
{
"summaryId": "11111111-2222-3333-4444-555555555555",
"title": "Goals & Success Criteria"
}
Variations
- Case-insensitive exact match:
title: { equalToInsensitive: $title } - Substring match:
title: { includesInsensitive: $title }
7. Update a section
Section content is updated via updateSection. You provide the section’s id plus a patch object containing only the fields you want to change.
Mutation
mutation UpdateSection($id: String!, $patch: SectionPatch!) {
updateSection(input: { id: $id, patch: $patch }) {
section {
id
title
type
order
data
updatedAt
}
}
}
Variables
{
"id": "aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee",
"patch": {
"title": "Updated title",
"data": { "content": "New body content goes here" }
}
}
Editable fields on SectionPatch
| Field | Type | Notes |
|---|---|---|
title |
String |
Display title of the section. |
data |
JSON |
Section payload — shape depends on type. Pass the full new value, not a partial diff. |
order |
BigFloat |
Display order. Use a value between two existing siblings to reposition. |
type |
String |
Section type. Generally not changed after creation. |
internal |
Boolean |
Whether the section is internal-only or customer-visible. |
Important:
datais replaced wholesale. To make a small change, fetch the section first (query #5 or #6), modify the JSON locally, then send the full updated object back inpatch.data.
End-to-end example with cURL
Look up an accord by CRM opportunity ID, fetch its first summary, find a section called “Goals”, and update its body — using only the API.
Step 1 — find the accord
curl -X POST <https://api.inaccord.com/graphql> \\
-H "authorization: Bearer$ACCORD_API_KEY" \\
-H "Content-Type: application/json" \\
-d '{
"query": "query($crmId: String!) { accords(filter: { crmId: { equalTo: $crmId }, deletedAt: { isNull: true } }, first: 1) { id accountName } }",
"variables": { "crmId": "0061a000003abcdAAA" }
}'
Step 2 — get summaries
curl -X POST <https://api.inaccord.com/graphql> \\
-H "authorization: Bearer$ACCORD_API_KEY" \\
-H "Content-Type: application/json" \\
-d '{
"query": "query($accordId: String!) { summaries(filter: { accordId: { equalTo: $accordId }, deletedAt: { isNull: true } }, orderBy: [ORDER_ASC]) { id title } }",
"variables": { "accordId": "<ACCORD_ID_FROM_STEP_1>" }
}'
Step 3 — get the “Goals” section
curl -X POST <https://api.inaccord.com/graphql> \\
-H "authorization: Bearer$ACCORD_API_KEY" \\
-H "Content-Type: application/json" \\
-d '{
"query": "query($summaryId: String!, $title: String!) { sections(filter: { summaryId: { equalTo: $summaryId }, title: { equalTo: $title }, deletedAt: { isNull: true } }, first: 1) { id data } }",
"variables": {
"summaryId": "<SUMMARY_ID_FROM_STEP_2>",
"title": "Goals"
}
}'
Step 4 — update the section
curl -X POST <https://api.inaccord.com/graphql> \\
-H "authorization: Bearer$ACCORD_API_KEY" \\
-H "Content-Type: application/json" \\
-d '{
"query": "mutation($id: String!, $patch: SectionPatch!) { updateSection(input: { id: $id, patch: $patch }) { section { id title updatedAt } } }",
"variables": {
"id": "<SECTION_ID_FROM_STEP_3>",
"patch": { "data": { "content": "Land 3 enterprise pilots by Q2." } }
}
}'
Error handling
GraphQL responses always return HTTP 200 (unless transport-level errors occur). Check for an errors array in the response body:
{
"data": null,
"errors": [
{ "message": "permission denied", "path": ["updateSection"] }
]
}
Common causes:
permission denied— the API key lacks access to the workspace or record.Cannot return null for non-nullable field— usually means the parent record was not found; check IDs.Variable "$x" of required type ... was not provided— aString!variable was sent asnull. Either provide a value or omit the corresponding clause from your filter.