> ## Documentation Index
> Fetch the complete documentation index at: https://docs.api.rask.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# ETags and If-Match

> Edit Rask API v3 segments and speakers safely: read the ETag, send If-Match, and what 412, 428 and the busy conflicts mean.

Segments, speakers and translation settings carry an opaque, strong ETag, in the `ETag` header and in the `etag` field of the body, so lists have it too. A write commits only while the resource still matches the ETag you read, so two writers cannot silently overwrite each other.

## Edit a segment

Read it, then send its `etag` unchanged, quotes included, in `If-Match`.

```typescript TypeScript theme={null}
const { data: segment } = await rask.segments.getSegment({ path: { project_id: projectId, segment_id: segmentId } });

const { data: transcriptEditResult } = await rask.segments.updateSegment({
  path: { project_id: projectId, segment_id: segmentId },
  body: { translation_text: 'Hoy repasamos las novedades de doblaje.' },
  ifMatch: '"s-a197265875140819"',
});
```

```python Python theme={null}
segment = client.segments.get_segment(project_id, segment_id)

transcript_edit_result = client.segments.update_segment(
    project_id, segment_id, if_match='"s-a197265875140819"', translation_text="Hoy repasamos las novedades de doblaje."
)
```

```go Go theme={null}
segment, err := client.Segments.GetSegment(ctx, projectID, segmentID, nil)
if err != nil {
	return err
}

transcriptEditResult, err := client.Segments.UpdateSegment(
	ctx,
	projectID,
	segmentID,
	`"s-a197265875140819"`,
	api.UpdateSegmentRequest{TranslationText: rask.Ptr("Hoy repasamos las novedades de doblaje.")},
)
if err != nil {
	return err
}
```

```java Java theme={null}
Segment segment = rask.segments().getSegment(projectId, segmentId).data();

TranscriptEditResult transcriptEditResult = rask.segments().updateSegment(
        projectId,
        segmentId,
        "\"s-a197265875140819\"",
        UpdateSegmentRequest.builder().translationText("Hoy repasamos las novedades de doblaje.").build()).data();
```

```php PHP theme={null}
$segment = $rask->segments->getSegment($projectId, $segmentId);

$transcriptEditResult = $rask->segments->updateSegment(
    $projectId,
    $segmentId,
    ['translation_text' => 'Hoy repasamos las novedades de doblaje.'],
    ['if_match' => '"s-a197265875140819"'],
);
```

```bash cURL theme={null}
curl "https://api.rask.ai/v3/projects/$PROJECT_ID/segments/$SEGMENT_ID" \
  -H "Authorization: Bearer $RASK_API_KEY"

curl -X PATCH "https://api.rask.ai/v3/projects/$PROJECT_ID/segments/$SEGMENT_ID" \
  -H "Authorization: Bearer $RASK_API_KEY" \
  -H "Content-Type: application/json" \
  -H 'If-Match: "s-a197265875140819"' \
  -d '{"translation_text": "Hoy repasamos las novedades de doblaje."}'
```

The answer holds the changed segments and `effects`: which segments need a redub, whether a translation started (`effects.retranslation`), and whether a restore point was saved. If a translation started, wait for its job before you dub. Then [estimateDub](https://docs.api.rask.ai/operations/estimateDub.md) with `mode: changed_segments` prices the redub.

## Which operations take what

| Operations | Condition |
| - | - |
| [updateSegment](https://docs.api.rask.ai/operations/updateSegment.md), [deleteSegment](https://docs.api.rask.ai/operations/deleteSegment.md), [splitSegment](https://docs.api.rask.ai/operations/splitSegment.md), [selectTranslationVariant](https://docs.api.rask.ai/operations/selectTranslationVariant.md) | `If-Match` with the segment's ETag, required |
| [updateSpeakerVoice](https://docs.api.rask.ai/operations/updateSpeakerVoice.md) | `If-Match` with the speaker's ETag, required |
| [mergeSegments](https://docs.api.rask.ai/operations/mergeSegments.md) | Each segment's ETag in the body, in `segments[].etag`. No header |
| [updateTranslationSettings](https://docs.api.rask.ai/operations/updateTranslationSettings.md) | `If-Match` optional; a stale value fails |
| [createSegment](https://docs.api.rask.ai/operations/createSegment.md) | No ETag: an insert. Requires an `Idempotency-Key` |
| [retranslateSegments](https://docs.api.rask.ai/operations/retranslateSegments.md), [importTranscript](https://docs.api.rask.ai/operations/importTranscript.md), [restoreTranscriptVersion](https://docs.api.rask.ai/operations/restoreTranscriptVersion.md) | No ETag: the whole transcript has none. They require an `Idempotency-Key`, are refused while processing or a dub runs, and save a restore point first. A retranslation of fewer than 10 chosen segments saves none |
| Projects, folders, glossaries, voice clones | No ETag: the last writer wins |

## What the answers mean

| Answer | Meaning | What to do |
| - | - | - |
| 428 [`precondition_required`](https://docs.api.rask.ai/errors/precondition_required) | `If-Match` is missing, or it is `*` | Read the resource and send its ETag |
| 412 [`precondition_failed`](https://docs.api.rask.ai/errors/precondition_failed) | The resource changed. `current_etag` holds the new ETag | Read it again, apply your change to what is there now, and retry once |
| 409 [`project_not_ready`](https://docs.api.rask.ai/errors/project_not_ready) | Processing or a whole-transcript job runs | Wait for the job in `job_id` |
| 409 [`dub_in_progress`](https://docs.api.rask.ai/errors/dub_in_progress) | A dub runs. Edits stay refused until it merges or fails | Wait for the job in `job_id` |
| 409 [`segment_busy`](https://docs.api.rask.ai/errors/segment_busy) | Segments in `segment_ids` are translating or voicing | Wait, then retry |
| 409 [`transcript_busy`](https://docs.api.rask.ai/errors/transcript_busy) | Concurrent writes kept conflicting. For [importTranscript](https://docs.api.rask.ai/operations/importTranscript.md) and [restoreTranscriptVersion](https://docs.api.rask.ai/operations/restoreTranscriptVersion.md), also: a translation, a segment generation or a lip-sync runs, or a segment is being translated or voiced. Jobs started through the API are named in `active_job_ids` | Wait for the jobs in `active_job_ids`, if any, then retry after `Retry-After` |

In `project_not_ready` and `dub_in_progress`, `job_id` is null when the run was started outside the API, for example in the web app. Then read the project with [getProject](https://docs.api.rask.ai/operations/getProject.md) and retry later.

> [!WARNING]
>
> On a 412, do not resend the old values over the new state. Read the segment, decide what your change means now, and apply that.

## What changes an ETag

A segment's ETag is a hash of its editable content: times, speaker, source and translation text, review flag, selected variant, and an audio marker. So a segment generation or a dub can change it, even though you edited nothing. A `SegmentClip` holds the ETag right after synthesis in `segment_etag`: read the segment again before you edit it.

A job's `ETag` is different: it is only a header of [getJob](https://docs.api.rask.ai/operations/getJob.md), for `If-None-Match`. See [Jobs](https://docs.api.rask.ai/v3/concepts/jobs.md#waiting).


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.