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

# POST /jobs/[id]/align

> Align, or re-align, a job that already exists

## Endpoint

```
POST https://lyrcs.ai/api/v1/jobs/{job_id}/align
```

Runs alignment on a job you already submitted. Two situations, one endpoint:

| The job                         | What happens                                           |
| ------------------------------- | ------------------------------------------------------ |
| Transcript only (`align=false`) | Alignment runs for the first time                      |
| Already aligned                 | Timings are **re-derived** from the job's current text |

The second case matters after an edit. Word-level timings are positional, so correcting a line leaves them describing words that are no longer there; re-aligning re-derives every output together so they cannot disagree.

## It does not charge

Your credit is bound to the song when the job is created, at your organisation's tier — the tier is an entitlement, not a per-feature price. A transcript-only job on Lyrics Pro has already paid the price that includes alignment, so aligning it later costs nothing further.

Alignment does require Lyrics Pro; a Lyrics Basic organisation receives `402`.

## Request

No body. The job id in the path is the whole request.

```bash theme={null}
curl -X POST https://lyrcs.ai/api/v1/jobs/{job_id}/align \
  -H "Authorization: Bearer $LYRCS_API_KEY"
```

## Responses

**Alignment queued** — `202`:

```json theme={null}
{
  "job_id": "a1b2c3d4-...",
  "status": "queued",
  "message": "Alignment queued. This job will report `processing` until timings are ready; poll GET /v1/jobs/{id}."
}
```

**Re-aligned** — `200`, synchronous:

```json theme={null}
{
  "job_id": "a1b2c3d4-...",
  "status": "complete",
  "realigned": true,
  "lines": 19,
  "words": 60,
  "coverage": 1
}
```

<Warning>
  **This is the one thing that moves a job out of `complete`.** A transcript-only
  job is `complete`; queueing alignment returns it to `processing` until the
  timings land. Everywhere else `complete` is terminal — here your own request
  changed what the job is for.
</Warning>

Completion is observed by polling `GET /jobs/{id}`. No new `job.complete` webhook is fired for work requested this way.

## Repeat calls are cheap

Re-aligning text that has already been timed would produce an identical result, so it is not done. A repeat call on unchanged text returns immediately:

```json theme={null}
{
  "job_id": "a1b2c3d4-...",
  "status": "complete",
  "realigned": false,
  "reason": "unchanged",
  "message": "The timings already describe this text. Edit the lyrics first if you want them re-derived."
}
```

Edit the lyrics — at the review gate, or in the studio — and the next call re-derives normally. A job whose timings came from the older aligner is always re-aligned, unchanged text or not, because switching aligner genuinely changes the result.

<Note>
  This endpoint draws on the **submit** rate limit, not the read limit, because it
  performs real work. Status polling is unaffected.
</Note>

## Errors

| HTTP | `code`    | Meaning                                                                            |
| ---- | --------- | ---------------------------------------------------------------------------------- |
| 402  | `PAY_002` | Alignment requires a Lyrics Pro plan                                               |
| 404  | `NOT_001` | Job not found, or belongs to another organisation                                  |
| 409  | `ALN_001` | No transcript yet — wait for transcription to finish                               |
| 409  | `ALN_002` | Job is held for review; approve it to release alignment                            |
| 409  | `ALN_003` | Re-alignment unavailable, or produced no usable result. Existing timings unchanged |
| 409  | `ALN_004` | Alignment is already running for this job                                          |
| 422  | `ALN_005` | No stored audio to align against                                                   |

A failed re-alignment never leaves you worse off: nothing is written unless the new timings are usable.

## Relationship to the withdrawn `POST /v1/align`

The old endpoint took audio and lyrics and created a *second* job for a recording already in the system — a second charge for one song. It never worked and returns `410`. This endpoint is its replacement, and takes a job id precisely so there is one row, one charge, and audio already in the bucket.
