Track and recover API tasks
A submission starts work. A query observes that same work. Keep the task identity so a slow result or lost connection does not become a second paid request.
Keep one task identity through the lifecycle
| Stage | What your client should retain |
|---|---|
| Before submission | The capability, original request, and stable Idempotency-Key header for REST, or idempotencyKey for MCP. |
| After acceptance | The returned task identifier and its field name, plus the response and tracing identifier. |
| While waiting | The same capability and task ID; follow any returned retry guidance. |
| At completion | The result URL and final task status. |
| After an uncertain outcome | The original identifiers and recovery instructions, even if your local request timed out. |
For REST, submit with POST /api/v1/<capability>/submit and query with POST /api/v1/<capability>/query. Most query bodies use {"taskId":"..."}. image-watermark-remover and watermark-removal use {"requestId":"..."}. Read the identifier field from the selected reference; X-Request-Id is a separate HTTP tracing header.
Give each submission a stable identity
Create a fresh unique Idempotency-Key before the first REST submit and store it with the original body. Send the same header on a transport retry of that exact logical request. A lost response is a reason to recover recent tasks, not to invent a new key. The examples use VIDMAGE_IDEMPOTENCY_KEY as an environment variable for this value.
Idempotency-Key: YOUR_STABLE_REQUEST_IDRead status before result
Published examples allow status at the response root or inside data. They recognize completion values success, succeeded, and completed, plus failure values failed and error. These are the values handled in the examples, not a promise that every intermediate state is listed here.
| Capability | Result field |
|---|---|
face-swap | imageUrl |
video-face-swap | videoUrl |
seedance-2-5-ai-video-generator | videoUrl |
The current snippets check result, then the capability result field at the response root or under data. For other image, video, GIF, audio, and 3D operations, use the result field in the capability directory. If a completed response has no result, keep the task ID and follow the service recovery response. Do not create a new task to repair a missing result.
Give polling a local time budget
The published REST examples poll at five-second intervals. Honor Retry-After on rate limits. MCP uses the returned retryAfterMs. A local wait limit should pause observation while preserving the task ID; it should not trigger a new submission.
# Query a task you already submitted.
# Replace both the capability and task ID with the original values.
curl --fail-with-body --silent --show-error \
-X POST "https://vidmage.ai/api/v1/face-swap/query" \
-H "Authorization: Bearer ${VIDMAGE_API_KEY}" \
-H "Content-Type: application/json" \
--data-raw '{"taskId":"YOUR_EXISTING_TASK_ID"}'This Python example observes an existing task for a bounded wait. Install requests first. It never creates a generation request. HTTP errors stop the loop so your caller can honor Retry-After and the specific recovery action before resuming.
import os
import time
import requests
capability = "face-swap"
task_id_field = "taskId" # Use requestId for the two watermark-removal routes.
result_field = "imageUrl" # Read the field from the capability reference.
task_id = "YOUR_EXISTING_TASK_ID"
headers = {"Authorization": f"Bearer {os.environ['VIDMAGE_API_KEY']}"}
deadline = time.monotonic() + 300
while time.monotonic() < deadline:
time.sleep(5)
response = requests.post(
f"https://vidmage.ai/api/v1/{capability}/query",
headers=headers,
json={task_id_field: task_id},
timeout=30,
)
# On HTTP failure, retain task_id and follow errorType and recovery.
response.raise_for_status()
payload = response.json()
if not payload.get("success"):
raise RuntimeError(payload)
data = payload.get("data") or payload
status = str(data.get("status") or "").lower()
if status == "needs_input":
raise RuntimeError(f"Face selection required; preserve this task: {task_id}")
if status in {"failed", "error"}:
raise RuntimeError(data.get("errorDetail") or data.get("error") or data)
if status in {"success", "succeeded", "completed"}:
result = payload.get("result") or payload.get(result_field) or data.get(result_field)
if not result:
raise RuntimeError(f"Result missing. Recover the same task: {task_id}")
print(result)
break
else:
raise TimeoutError(f"Local wait ended. Continue querying the same task: {task_id}")Recover after a lost submit response
If a timeout or disconnect hides the submission result, check recent tasks before creating another request. The service provides an authenticated recovery endpoint:
curl --fail-with-body --silent --show-error \
-H "Authorization: Bearer ${VIDMAGE_API_KEY}" \
"https://vidmage.ai/api/v1/tasks/recent?limit=10"Use the recovered task ID with the original capability query endpoint. If the outcome remains unknown, preserve the original idempotency value and follow recovery instructions. A new idempotency key is for a new generation request, not a timeout retry.
When waiting needs a different action
NEEDS_INPUTin MCP: show the face previews, make the requested selection withselect_faces, then continue the same task.TASK_QUERY_INTERRUPTEDorRESULT_MISSING: resume observation of the existing task.- Unknown submission, billing, or refund outcome: use the specific recovery action before any retry.
- Confirmed task failure: inspect the error and billing outcome before deciding whether to create a corrected request.
REST face-swap integrations can use POST /api/v1/face-analysis before submission and pass the chosen indexes in the request. If an existing task needs input, stop polling and preserve it; select_faces is the MCP resume tool, not a REST capability endpoint.
