---
canonical_url: "https://buildwithwave.com/docs/developers/errors-and-limits"
title: "Errors and retries"
description: "Where to send a Wave key, how to retry a write safely, and what to do when Wave answers with a message instead of a result."
group: "developers"
lane: "reference"
status: "published"
publishedAt: 2026-10-03
updatedAt: 2026-10-09
sidebarOrder: 70
tags: ["developer", "errors", "retries", "mcp"]
relatedSlugs:
  - "developers/what-you-can-ask-for"
  - "developers/safety"
  - "account/developer-api-keys"
  - "billing/estimate-credits"
sidebarLabel: "Errors and retries"
---

When a request needs something changed, Wave answers with a short message, a `code`, a `next_step`, and a `request_id`. Your assistant reads that and tells you what to do in plain language. The table below lists common messages and the next step for each.

## Where to send the key

An assistant connects over MCP at `https://wave-mcp.prod.buildwithwave.com/mcp`, with the key sent as `Authorization: Bearer` and the key. MCP is the right choice for an AI assistant.

Scripts and integrations use the same key on Wave's HTTP API at `https://api.go.buildwithwave.com/api/v1`. See [API examples](/docs/developers/api-examples).

When Wave says the key only works on the Wave API, the request reached an app page instead. Send the key to the MCP server or to `/api/v1`.

## Retry a write

Every write carries an `operation_id`, except a request for a photo upload link. Send the same `operation_id` with the same details, and Wave returns the first result without saving twice. A new action uses a new `operation_id`. The same photo file sent to a job twice is added once.

If Wave asks you to slow down, wait about a minute and send the same request again.

## Locked estimates

Sharing an estimate locks its rooms and lines. Drying readings, drying notes, and job notes keep working on that job. To change the estimate itself, unlock it in Wave.

## What to do for each message

{/* BEGIN GENERATED ERROR TABLE */}

| Code | What Wave says | What to do |
| --- | --- | --- |
| `invalid_key` | This key is not valid. | Create a new key in Settings, then Developer, and paste it into your AI tool. |
| `key_revoked` | This key was revoked. | Create a new key in Settings, then Developer, and update your AI tool. |
| `key_expired` | This key has expired. | Create a new key in Settings, then Developer. You can pick 30 days, 90 days, or 1 year. |
| `wrong_surface` | This key only works on the Wave API. | Send the key to the Wave MCP server or to /api/v1. See [Where to send the key](/docs/developers/errors-and-limits#where-to-send-the-key). |
| `read_only_key` | This key can only read. | Create a Write key in Settings, then Developer, and use it for changes. |
| `not_found` | That record was not found. | Check the job, customer, or estimate you named, and look it up again. |
| `validation_failed` | Something in the request is not valid. | Adjust the details and send it again. For a line from Wave's catalog, search the catalog and use a code from it. |
| `unknown_referral_source` | That referral source is not on this company's list. | Ask your AI to list the company's referral sources and pick one. |
| `estimate_locked` | This estimate is locked because it was shared, so its rooms and lines cannot change. Drying readings, drying notes, and job notes still work. | Unlock the estimate in Wave to change its rooms or lines. |
| `estimate_conflict` | The estimate changed since you read it. | Ask your AI to read the estimate again and apply the change to the current version. |
| `job_conflict` | The job changed since you read it. | Ask your AI to read the job again and send the change once more. |
| `insufficient_credits` | This company does not have a credit for that. | Add estimate credits in Wave, then ask again. The same request finishes once a credit is there. |
| `rate_limited` | Too many requests right now. | Wait about a minute, then send the same request again. |
| `idempotency_conflict` | That operation id was already used with different details. | Send the original request unchanged, or start the new one with a new operation_id. |
| `degraded` | Wave could not read or save that just now. | Wait a moment and try again. Open the job in Wave to see whether the change saved. |
| `internal` | Wave hit an unexpected error. | Try once more. If it happens again, contact Wave support. |

{/* END GENERATED ERROR TABLE */}

## Related

- [What you can ask for](/docs/developers/what-you-can-ask-for): jobs, photos, notes, estimates, and drying logs
- [Safety](/docs/developers/safety): Read keys, Write keys, and revoke
- [API keys](/docs/account/developer-api-keys): create a new key when one has ended
- [Estimate credits](/docs/billing/estimate-credits): the credit an estimate uses in Wave
