Updating Data

Optimistic Concurrency Control (OCC)

Overview

Sivo APIs implement Optimistic Concurrency Control (OCC) for PATCH requests. When data is retrieved, used by a client application, and subsequently updated there is a risk of a race condition where another update happens in between. This can result in data loss and unexpected behavior.

The If-Match HTTP header is used for optimistic concurrency control in PATCH APIs. It ensures that updates to resources are only applied if the resource hasn't been modified since the client last retrieved it.

Header Format

if-match: <datetime-string>

Usage

The if-match header is required for all PATCH requests that modify existing resources. The value should be the updated_at timestamp of the resource being updated.

Example Request

// Example using fetch
const response = await fetch('https://core.staging.sivo.com/buyers/123', {
  method: 'PATCH',
  headers: {
    'Content-Type': 'application/json',
    'if-match': '2025-07-09T18:30:00.000Z' // The last known updated_at timestamp
  },
  body: JSON.stringify({
    // Your update fields here
  })
});

if (!response.ok) {
  if (response.status === 412) {
    console.error('Resource was modified. Please refresh and try again.');
  }
  // Handle other errors
}

Error Responses

400 Bad Request

Cause: Missing or invalid if-match header
Solution: Include a valid ISO 8601 datetime string in the if-match header

412 Precondition Failed

Cause: The resource has been modified since the provided timestamp

Solution:

  1. Fetch the latest version of the resource,
  2. Re-apply your changes to the latest version
  3. Resubmit with the new updated_at timestamp

Best Practices

For workflows that retrieve and update a resource:

  1. Retrieve the object and retain the value of the updated_at field.
  2. Include the timestamp in the if-match header for any PATCH requests.
  3. Handle 412 errors by refreshing the resource and re-applying changes (if needed)
  4. Use the latest updated_at timestamp in the next PATCH attempt.

Client-Side Implementation

class ApiClient {
  private lastUpdated: string | null = null;

  async updateBuyer(id: string, data: Partial<Buyer>): Promise<Buyer> {
    if (!this.lastUpdated) {
      throw new Error('Must fetch buyer before updating');
    }

    const response = await fetch(`/buyers/${id}`, {
      method: 'PATCH',
      headers: {
        'Content-Type': 'application/json',
        'if-match': this.lastUpdated
      },
      body: JSON.stringify(data)
    });

    if (response.status === 412) {
      // Handle concurrency conflict
      throw new Error('Resource was modified. Please refresh and try again.');
    }

    if (!response.ok) {
      throw new Error('Failed to update buyer');
    }

    const updatedBuyer = await response.json();
    this.lastUpdated = updatedBuyer.updated_at;
    return updatedBuyer;
  }
}