Creating and updating data
Changes go through mutations. Every object has a create and an update mutation that work the same way, and deleting is an update too. Changes made through the API show up in the app straight away, for everyone on the team.
Create an item
Create mutations are named create plus the object: createTodo, createIssue, createRock. The new item's fields go inside input, under the object's name, and you choose which fields of the new item to get back:
mutation AddTodo($todo: TodoInput!) {
createTodo(input: { todo: $todo }) {
todo {
id
name
dueDate
}
}
}
{
"todo": {
"name": "Send the Q4 board pack",
"desc": "",
"teamId": "0b7e5f6a-3c2d-4e1f-9a8b-7c6d5e4f3a21",
"userId": "5f2d8c4b-1a3e-4c7d-b9f0-2e6a7d8c9b10",
"todoStatusId": "TODO",
"priorityNo": 999,
"dueDate": "2026-11-20",
"companyId": "6a1d4e2b-7c3f-4a8e-b5d9-0f1e2d3c4b5a"
}
}
A few things every create needs:
companyIdon every new item. Get it once withquery { currentCompanyId }or from the API Keys page.- The owner and team, as IDs (
userId,teamId). Look them up withusersandteamsqueries. - The object's required fields. The Data model guide lists them for each object, and the error message names any you've missed.
Leave out id, createdAt and stateId, and they're filled in for you. You can set id yourself (any UUID) when you want to refer to the new item in the same request; see Make several changes at once.
Update an item
Update mutations are named update plus the object. Pass the item's id, and in patch only the fields you want to change. Everything else stays as it is:
mutation MarkTodoDone($id: UUID!) {
updateTodo(input: { id: $id, patch: { todoStatusId: "COMPLETE" } }) {
todo { id name todoStatusId }
}
}
Some common updates:
| To do this | Patch |
|---|---|
| Mark a To-do done | updateTodo with { todoStatusId: "COMPLETE" } |
| Solve an Issue | updateIssue with { issueStatusId: "COMPLETE" } |
| Set a Rock off-track | updateRock with { rockStatusId: "OFFTRACK" } |
| Move an Issue to another team | updateIssue with { teamId: "..." } |
| Change a due date | { dueDate: "2026-12-05" } |
| Hand an item to someone else | { userId: "..." } |
When you change a status, also set statusUpdatedAt to the current time, as the app does. Success.co uses it for things like when a To-do was completed, and it isn't updated for you:
mutation MarkTodoDone($id: UUID!, $now: Datetime!) {
updateTodo(input: { id: $id, patch: { todoStatusId: "COMPLETE", statusUpdatedAt: $now } }) {
todo { id todoStatusId statusUpdatedAt }
}
}
Delete an item
Rocks, To-dos, Issues, Headlines, Milestones, Scorecard measurables and values, comments and the other main objects don't have delete mutations. Deleting one is an update that sets stateId to DELETED, exactly what the app does when someone clicks Delete:
mutation DeleteIssue($id: String!) {
updateIssue(input: { id: $id, patch: { stateId: "DELETED" } }) {
issue { id stateId }
}
}
The item disappears from the app but stays in the API with stateId: "DELETED", which is why list queries should filter on stateId: { equalTo: "ACTIVE" }. To restore it, set stateId back to ACTIVE.
A few linking records (such as which teams a process is shared with) do have delete mutations, and those remove the record for good.
Make several changes at once
You can put more than one mutation in a request. Give each an alias so you can tell the answers apart:
mutation {
first: updateTodo(input: { id: "3f1c...", patch: { todoStatusId: "COMPLETE" } }) { todo { id } }
second: updateTodo(input: { id: "9a4e...", patch: { todoStatusId: "COMPLETE" } }) { todo { id } }
}
The mutations in one request run as a single unit: if any of them fails, none of them are saved. That makes it safe to create things that belong together, like a Rock and its link to a team. Choose the Rock's id yourself (a fresh UUID) so the second mutation can refer to it:
mutation AddRock($rockId: UUID!, $teamId: UUID!, $userId: UUID!, $companyId: UUID!) {
createRock(
input: {
rock: {
id: $rockId
name: "Launch the partner program"
desc: ""
type: "company"
rockStatusId: "ONTRACK"
dueDate: "2026-12-31"
userId: $userId
companyId: $companyId
}
}
) {
rock { id }
}
createTeamsOnRock(
input: { teamsOnRock: { rockId: $rockId, teamId: $teamId, companyId: $companyId } }
) {
teamsOnRock { id }
}
}
In JavaScript, crypto.randomUUID() makes a UUID; in Python, str(uuid.uuid4()).
Who made the change
Changes are made as the person who owns the API key. The app shows them as that person's, and lastUpdatedByUserId on the item records it. If your integration makes a lot of changes, consider creating a separate Success.co user for it (for example "Finance sync", which counts as a user on your plan) and making its key there, so its changes are easy to tell apart from yours. It also keeps the integration running if you leave: a key stops working when its owner is deactivated, so an integration that runs on a person's key stops when they do. Keep that user active for as long as the integration runs.
Test on a copy first. There's no undo for changes made through the API, apart from setting them back yourself. Try new scripts against a test company, or on a team set up for testing, before pointing them at your real data.