Markup Generation

The markup generator creates property boundary markup documents using aerial imagery. Unlike single-shot endpoints, this is a multi-step flow requiring human input between phases.

Overview

The markup flow has 5 steps:

StepEndpointWhat Happens
1POST /v1/markup/generateStart generation -- captures aerial screenshots and enhances with AI
2GET /v1/markup/jobs/{jobId}Poll until awaiting_input -- orientation variants are ready
3POST /v1/markup/jobs/{jobId}/selectSelect which orientation to use
4POST /v1/markup/jobs/{jobId}/annotateSubmit annotated boundary + clean image
5POST /v1/markup/jobs/{jobId}/video(Optional) Generate cinematic video

Flow Diagram

POST /v1/markup/generate
  |
  v
Job created (status: queued)
  |
  v
Background processing...
  |
  v
Status: awaiting_input
(orientation_variants available)
  |
  v
POST /v1/markup/jobs/{id}/select
  |
  v
Status: running (annotation_ready)
  |
  v
POST /v1/markup/jobs/{id}/annotate
  |
  v
Status: completed
  |
  v (optional)
POST /v1/markup/jobs/{id}/video

Step 1: Start Generation

curl -X POST \
  -H "X-API-Key: dome_live_your_key" \
  -H "Content-Type: application/json" \
  -d '{"address": "42 Wallaby Way, Sydney NSW 2000"}' \
  https://api.domeagent.com.au/v1/markup/generate

Behind the scenes, the system captures aerial screenshots from multiple angles and enhances them with AI. This typically takes 30-90 seconds.

Step 2: Poll for Orientation Variants

Poll every 5 seconds until status changes to awaiting_input. The response includes orientation variants:

{
  "data": {
    "id": "d56ef78a-...",
    "status": "awaiting_input",
    "output": {
      "orientation_variants": [
        {
          "key": "bottom",
          "preview_url": "https://storage.domeagent.com.au/markup/...-bottom-preview.jpg",
          "enhanced_url": "https://storage.domeagent.com.au/markup/...-bottom-enhanced.jpg"
        },
        {
          "key": "left",
          "preview_url": "https://storage.domeagent.com.au/markup/...-left-preview.jpg",
          "enhanced_url": "https://storage.domeagent.com.au/markup/...-left-enhanced.jpg"
        }
      ]
    }
  }
}

Display the preview_url images to the user so they can choose the best aerial view.

Step 3: Select Orientation

curl -X POST \
  -H "X-API-Key: dome_live_your_key" \
  -H "Content-Type: application/json" \
  -d '{"orientation": "bottom"}' \
  https://api.domeagent.com.au/v1/markup/jobs/{jobId}/select

The response includes a selected_url -- the high-resolution enhanced image for annotation.

Step 4: Submit Annotation

After the user draws property boundaries on the selected image, submit the annotated and clean image URLs:

curl -X POST \
  -H "X-API-Key: dome_live_your_key" \
  -H "Content-Type: application/json" \
  -d '{
    "annotated_image_url": "https://your-storage.com/annotated.jpg",
    "clean_image_url": "https://your-storage.com/clean.jpg"
  }' \
  https://api.domeagent.com.au/v1/markup/jobs/{jobId}/annotate

Step 5: Generate Video (Optional)

Generate a cinematic flyover video from the completed markup:

curl -X POST \
  -H "X-API-Key: dome_live_your_key" \
  -H "Content-Type: application/json" \
  -d '{}' \
  https://api.domeagent.com.au/v1/markup/jobs/{jobId}/video

This returns a new job ID. Poll GET /v1/jobs/{videoJobId} for the video result.

Complete Code Example

const API_KEY = "dome_live_your_key";
const BASE_URL = "https://api.domeagent.com.au/v1";

async function api(path, options) {
  const res = await fetch(`${BASE_URL}${path}`, {
    ...options,
    headers: {
      "X-API-Key": API_KEY,
      "Content-Type": "application/json",
      ...options?.headers,
    },
  });
  return res.json();
}

async function pollUntil(jobId, targetStatus) {
  while (true) {
    const { data } = await api(`/markup/jobs/${jobId}`);
    console.log(`Status: ${data.status}`);
    if (data.status === targetStatus) return data;
    if (data.status === "failed") throw new Error("Job failed");
    await new Promise((r) => setTimeout(r, 5000));
  }
}

// Step 1: Start generation
const { data: job } = await api("/markup/generate", {
  method: "POST",
  body: JSON.stringify({ address: "42 Wallaby Way, Sydney NSW 2000" }),
});

// Step 2: Wait for orientation variants
const awaitingJob = await pollUntil(job.job_id, "awaiting_input");
const variants = awaitingJob.output.orientation_variants;

// Step 3: Select orientation
const { data: selection } = await api(`/markup/jobs/${job.job_id}/select`, {
  method: "POST",
  body: JSON.stringify({ orientation: "bottom" }),
});

// Step 4: Submit annotation
await api(`/markup/jobs/${job.job_id}/annotate`, {
  method: "POST",
  body: JSON.stringify({
    annotated_image_url: "https://your-storage.com/annotated.jpg",
    clean_image_url: "https://your-storage.com/clean.jpg",
  }),
});

// Step 5: (Optional) Generate video
const { data: videoJob } = await api(`/markup/jobs/${job.job_id}/video`, {
  method: "POST",
  body: JSON.stringify({}),
});

UI Integration Tips

  • Step 1-2: Show a loading spinner while the job processes. Display a progress indicator.
  • Step 2-3: Display orientation preview images side-by-side. Let the user click to select.
  • Step 3-4: Show the selected high-res image with a boundary drawing canvas overlay.
  • Step 4: Upload the annotated and clean images to your own storage, then submit the URLs.
  • Step 5: Show a “Generate Video” button after annotation is complete.

Next Steps