Async estimates
An estimate with photos can take 10 to 30 seconds. With mode: async, the request answers 202 right away and the estimate finishes in the background. Read it later with GET /v1/estimates/{id}, or let a webhook tell you when it is ready.
mode is sync by default. Async mode works for text, conversations, photos and follow-ups, and costs the same credits.
Send the request
curl https://api.wfskills.com/v1/estimates \
-H "Authorization: Bearer $HOMETOWN_API_KEY" \
-H "Idempotency-Key: $(uuidgen)" \
-H "Content-Type: application/json" \
-d '{"mode": "async", "input": {"text": "The fence in the back yard", "images": [{"data": "<base64>"}]}}'
Validation, content review and the credit check run before the 202, so their errors answer at once, as in sync mode. The answer is 202 with the estimate in status: processing. Its id is final, and the X-Credits-Held header says how many credits are held for it.
{
"id": "est_01J9Z6X1M4QK2V7H8T3R5N0BCD",
"object": "estimate",
"status": "processing",
"environment": "live",
"region": "US-MS",
"currency": "USD",
"locale": "en-US",
"locale_fallback": null,
"task": null,
"scope": null,
"labor": null,
"price": null,
"calendar": null,
"steps": [],
"extras": [],
"unpriced": [],
"question": null,
"usage": {
"model_calls": 0,
"credits": 0
},
"metadata": {},
"created_at": "2026-10-09T15:04:05+00:00"
}
Sending the same request again with the same Idempotency-Key returns this first 202, not the current state. Read the estimate with GET to see how it is going.
Get the result
Most estimates finish within a minute. Use one of these:
- Listen for
estimate.completedandestimate.failedon a webhook endpoint. The event carries the whole estimate indata.object. - Poll
GET /v1/estimates/{id}every few seconds untilstatusispriced,needs_detail,unsupported,unclearorfailed.
What you pay
The credits are held when the request is accepted. They are spent when the estimate finishes with an answer (priced, needs_detail, unsupported or unclear) and given back when it fails. A test key holds nothing.
When an estimate fails
A failed estimate has status: failed, an error with a code and a detail, and costs nothing.
error.code | Meaning |
|---|---|
model_unavailable | The estimator did not answer in time. Send the request again with a new Idempotency-Key. |
unsupported_in_region | The job is not priced in the region. |
internal_error | Something went wrong on the Hometown side. Quote the estimate id if you contact support. |
An estimate still processing 15 minutes after the request fails with model_unavailable within the next 5 minutes.
Try it in the sandbox
With a test key, sandbox:async answers 202 and finishes as priced within seconds, and sandbox:failed finishes as failed. Both answer 202 whatever mode says, and send their event to the test endpoints that listen to estimate.completed or estimate.failed. See Sandbox inputs.