TeamGriddeveloper
TeamGrid Developer

Resource concurrency

Prevent lost updates with API v1 strong ETags and required If-Match preconditions.

API v1 uses explicit optimistic concurrency for protected writes. Read the resource, retain the strong ETag response header, and send that value unchanged as If-Match with the mutation. Never derive an ETag from an ID, timestamp, or another resource.

Projects, tasks, and project templates

The 1.1.0 contract exposes developerRevision and developerUpdatedAt on projects, tasks, and project templates. Their item reads and synchronous mutation responses include a strong ETag and Cache-Control: private, no-store, no-transform.

Exactly 18 core mutations require If-Match:

Resource Protected mutations
Projects Update, replace sharing, complete, reopen, archive, and restore
Tasks Update, duplicate, move, replace checklist, archive, restore, complete, and reopen
Project templates Update, archive, restore, and instantiate

Project lifecycle commands and template instantiation are asynchronous. Their accepted operation contains the immutable sourceRevision; a successful terminal operation contains resultRevision. Operation reads created before revision tracking return 410 rather than silently weakening the concurrency guarantee.

project=$(teamgrid projects get "$PROJECT_ID" --output json)
revision=$(printf '%s' "$project" | jq -er '.attributes.developerRevision')

teamgrid projects update "$PROJECT_ID" \
  --data '{"name":"Reviewed project"}' \
  --if-match "prj1-$revision"

The SDK also accepts the strong ETag returned in transport metadata:

const task = await teamgrid.tasks.get('task-id')
await teamgrid.tasks.update(
  task.data.id,
  { name: 'Reviewed task' },
  { ifMatch: task.transport.headers.etag },
)

Duplication also requires an idempotency key because it creates a new resource. Its precondition is the exact source revision, so retrying the same request cannot silently copy a newer task:

await teamgrid.tasks.duplicate(
  task.data.id,
  { copyChecklist: true, name: 'Reviewed task copy' },
  {
    idempotencyKey: 'review-copy-2026-07-27',
    ifMatch: task.transport.headers.etag,
  },
)

After a successful mutation, retain the newly returned ETag before making another change. Do not reuse the pre-mutation validator.

Other protected resources

Another 47 operations retain their domain-specific compare-and-set contracts:

Resource family Protected mutations
Appointments and absences 6
Comments 2
Documents and files 6
Custom-field values 2
Planned work 1
Recurring-task series, versions, templates, occurrences, and task detachment 13
Members, invitations, roles, and groups 8
Service-account resource grants 1
Time-entry billing 1
Automation definitions and runs 4
Workspace settings 1
Webhook configuration and secret rotation 2

Revision formats are intentionally resource-specific. Always use the validator returned by the corresponding read operation.

Failure contract

Status and code Meaning Action
400 invalid_precondition The validator is malformed, weak, duplicated, or belongs to another resource contract Correct the request using the latest endpoint response
412 precondition_failed The resource changed after it was read Re-read, reconcile, and retry only after a new decision
428 precondition_required A protected mutation omitted If-Match Read the resource and send its latest strong validator
503 service_unavailable The owning cell cannot currently prove the concurrency contract Keep the precondition and retry later with bounded backoff

Never respond to 412 or 503 by removing If-Match. This safety model is qualified per production cell and the API readiness check remains closed when resource concurrency cannot be proven.

Stable documentation · Developer Platform · Reviewed 2026-08-19Edit this page ↗
Documentation feedbackWas this page useful?
Esc

Search TeamGrid Developer

Find guides, concepts and every API operation.