GoliathDevelopersGet an API key
Reference/Dealsgenerated from the gateway manifest

updateDeal

Update a deal by id: title, price, close date, stage.

POST/api/v1/callWRITEidempotent · 24 hruns as the key's owner
View as Markdown

Update a deal by id: title, price, close date, stage. THIS IS THE STAGE-MOVE OPERATION: send input { stageId } alone (from listPipelineStages); fields left out are untouched, and the move stamps lastStageChangeAt. A stage change is POTENTIALLY OUTBOUND by TWO paths: (1) entering the stage wakes any DEAL workflow triggered on it; (2) it fires a deal-updated event for EVERY attached contact, and a CONTACT workflow anywhere in the org can match the destination stage, so a clean pipeline does NOT prove the move is silent. Either can text or email a real person. Do NOT reason "reversible, so just do it": undoing the move does not recall a message. A move the person asked for by name is their yes; any other move of a deal with contacts needs their yes before it runs (a card counts). The deal, stage and linked contactIds/userIds must belong to your organization (or a pipeline shared with it). contactIds, propertyIds and userIds REPLACE the whole linked set: send every id to keep. Pass expectedUpdatedAt (updatedAt from getDeal, findDeals or a previous write) whenever you decided from an earlier read, so a concurrent edit fails loudly. It makes the write conditional and ALL-OR-NOTHING: a change since gives CONFLICT with nothing written, and a rejected userIds, participantSplits or customFieldValues rolls the whole edit back. The token is single-use: re-read before guarding again. Without it a later custom-field failure cannot undo the committed core write. A deleted deal is NOT_FOUND (do not retry); a bad token is BAD_USER_INPUT. MONEY AND CUSTOM FIELDS (the whole input is one variable, so discovery cannot expand it). One commission source per side: commissionAmountCents XOR commissionPercentBps (basis points, 300 = 3%, of priceCents), and teamSplitAmountCents XOR teamSplitPercentBps (of the COMMISSION). Both halves with values is rejected; one sent as null CLEARS that side. The server derives commissionCents (GCI) and returns it: read it there. A percent re-derives when priceCents changes; an amount does not. participantSplits is [{ userId, splitAmountCents | splitPercentBps }] of what remains after the team fee, within the pool. customFieldValues is [{ customFieldId (listDealCustomFields; fields are PER-PIPELINE), exactly ONE of textValue (TEXT/LINK) | numberValue (NUMBER/DOLLAR, a plain number, NOT cents) | dateValue (DATE, ISO 8601) | dropdownSelectedValues (DROPDOWN option LABELS, not ids) }]. The response echoes the money and custom fields: confirm the write from it.

curl https://server.goliathdata.com/api/v1/call \
  -H "Authorization: Bearer $GOLIATH_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{
    "operationId": "updateDeal",
    "variables": {
      "dealId": "<dealId>",
      "input": {}
    }
  }'
import { GoliathClient } from '@goliath-data/sdk'

const goliath = new GoliathClient({
  apiKey: process.env.GOLIATH_API_KEY!,
  baseUrl: 'https://server.goliathdata.com',
})

const result = await goliath.deals.updateDeal(
  {
    dealId: '<dealId>',
    input: {}
  },
  { idempotencyKey: crypto.randomUUID() },
)
const res = await fetch('https://server.goliathdata.com/api/v1/call', {
  method: 'POST',
  headers: {
    Authorization: `Bearer ${process.env.GOLIATH_API_KEY}`,
    'Content-Type': 'application/json',
    'Idempotency-Key': crypto.randomUUID(),
  },
  body: JSON.stringify({
    operationId: 'updateDeal',
    variables: {
      dealId: '<dealId>',
      input: {}
    }
  }),
})
const { data, errors } = await res.json()

Variables

The example sends only what the schema requires. Some operations also need one of several optional variables (one filter or another, say); the description above says which.

dealIdID!required
inputPipelineItemMutationInput!required
titleString
stageIdID
descriptionString
priceCentsBigInt
commissionAmountCentsBigInt
commissionPercentBpsInt
teamSplitAmountCentsBigInt
teamSplitPercentBpsInt
participantSplits[PipelineItemParticipantSplitInput!]
closeDateDateTime
propertyIds[ID!]
propertyStrings[String!]
contactIds[ID!]
userIds[ID!]
fileIds[ID!]
orderInt
customFieldValues[PipelineItemCustomFieldValueInput!]
expectedUpdatedAtDateTime
Every field, all the way down
variables
[
  {
    "name": "dealId",
    "type": "ID!",
    "required": true,
    "description": null,
    "defaultValue": null
  },
  {
    "name": "input",
    "type": "PipelineItemMutationInput!",
    "required": true,
    "description": null,
    "defaultValue": null,
    "fields": [
      {
        "name": "title",
        "type": "String",
        "required": false,
        "description": null,
        "defaultValue": null
      },
      {
        "name": "stageId",
        "type": "ID",
        "required": false,
        "description": null,
        "defaultValue": null
      },
      {
        "name": "description",
        "type": "String",
        "required": false,
        "description": null,
        "defaultValue": null
      },
      {
        "name": "priceCents",
        "type": "BigInt",
        "required": false,
        "description": null,
        "defaultValue": null
      },
      {
        "name": "commissionAmountCents",
        "type": "BigInt",
        "required": false,
        "description": null,
        "defaultValue": null
      },
      {
        "name": "commissionPercentBps",
        "type": "Int",
        "required": false,
        "description": null,
        "defaultValue": null
      },
      {
        "name": "teamSplitAmountCents",
        "type": "BigInt",
        "required": false,
        "description": null,
        "defaultValue": null
      },
      {
        "name": "teamSplitPercentBps",
        "type": "Int",
        "required": false,
        "description": null,
        "defaultValue": null
      },
      {
        "name": "participantSplits",
        "type": "[PipelineItemParticipantSplitInput!]",
        "required": false,
        "description": null,
        "defaultValue": null,
        "fields": [
          {
            "name": "userId",
            "type": "ID!",
            "required": true,
            "description": null,
            "defaultValue": null
          },
          {
            "name": "splitAmountCents",
            "type": "BigInt",
            "required": false,
            "description": null,
            "defaultValue": null
          },
          {
            "name": "splitPercentBps",
            "type": "Int",
            "required": false,
            "description": null,
            "defaultValue": null
          }
        ]
      },
      {
        "name": "closeDate",
        "type": "DateTime",
        "required": false,
        "description": null,
        "defaultValue": null
      },
      {
        "name": "propertyIds",
        "type": "[ID!]",
        "required": false,
        "description": null,
        "defaultValue": null
      },
      {
        "name": "propertyStrings",
        "type": "[String!]",
        "required": false,
        "description": null,
        "defaultValue": null
      },
      {
        "name": "contactIds",
        "type": "[ID!]",
        "required": false,
        "description": null,
        "defaultValue": null
      },
      {
        "name": "userIds",
        "type": "[ID!]",
        "required": false,
        "description": null,
        "defaultValue": null
      },
      {
        "name": "fileIds",
        "type": "[ID!]",
        "required": false,
        "description": null,
        "defaultValue": null
      },
      {
        "name": "order",
        "type": "Int",
        "required": false,
        "description": null,
        "defaultValue": null
      },
      {
        "name": "customFieldValues",
        "type": "[PipelineItemCustomFieldValueInput!]",
        "required": false,
        "description": null,
        "defaultValue": null,
        "fields": [
          {
            "name": "customFieldId",
            "type": "ID!",
            "required": true,
            "description": null,
            "defaultValue": null
          },
          {
            "name": "textValue",
            "type": "String",
            "required": false,
            "description": null,
            "defaultValue": null
          },
          {
            "name": "dateValue",
            "type": "DateTime",
            "required": false,
            "description": null,
            "defaultValue": null
          },
          {
            "name": "numberValue",
            "type": "Float",
            "required": false,
            "description": null,
            "defaultValue": null
          },
          {
            "name": "dropdownSelectedValues",
            "type": "[String!]",
            "required": false,
            "description": null,
            "defaultValue": null
          }
        ]
      }
    ]
  },
  {
    "name": "expectedUpdatedAt",
    "type": "DateTime",
    "required": false,
    "description": null,
    "defaultValue": null
  }
]

Response

What comes back under data: a list shows as one entry, and a value as its type.

response shape
{
  "data": {
    "dealMutation": {
      "updateDeal": {
        "id": "<ID>",
        "title": "…",
        "priceCents": "…",
        "closeDate": "2026-01-01T00:00:00.000Z",
        "isArchived": true,
        "updatedAt": "2026-01-01T00:00:00.000Z",
        "lastStageChangeAt": "2026-01-01T00:00:00.000Z",
        "commissionCents": "…",
        "commissionAmountCents": "…",
        "commissionPercentBps": 0,
        "teamSplitAmountCents": "…",
        "teamSplitPercentBps": 0,
        "participantSplits": [
          {
            "user": {
              "id": "<ID>",
              "firstName": "…",
              "lastName": "…"
            },
            "splitAmountCents": "…",
            "splitPercentBps": 0
          }
        ],
        "customFieldValues": [
          {
            "id": "<ID>",
            "textValue": "…",
            "dateValue": "2026-01-01T00:00:00.000Z",
            "numberValue": 0,
            "dropdownSelectedValues": [
              "…"
            ],
            "dealCustomField": {
              "id": "<ID>",
              "name": "…",
              "type": "DROPDOWN"
            }
          }
        ],
        "stage": {
          "id": "<ID>",
          "name": "…"
        },
        "pipeline": {
          "id": "<ID>",
          "name": "…"
        },
        "contacts": [
          {
            "id": "<ID>",
            "name": "…"
          }
        ],
        "properties": [
          {
            "id": "<ID>",
            "address": {
              "addressFull": "…"
            }
          }
        ]
      }
    }
  }
}

Errors

Idempotency

Send an Idempotency-Key of 1 to 255 characters. A finished request retried with the same key replays its stored response, marked Idempotency-Replayed: true; one still in flight returns 409 idempotency_conflict. Keys expire after 24 hours. Write without fear →