Open Application Platform Docs

Territory design (beta)

Cut weighted points into compact, non-overlapping territories that each carry a balanced share of the work.

territory.design Beta point_1k

How to call it

REST

POST /v1/territory/design

GraphQL

mutation openTerritoryDesign

MCP

open_territory_design

Cut weighted points (stops, addresses, postcodes with their demand) into compact, non-overlapping territories that each carry a balanced share of the total weight. Ask for a number of territories or name them with optional anchors and shares. Distances are straight-line. Billed per point.

Request schema

Generated from the same validation rules the endpoint runs, so a field cannot be documented without being validated.

POST /v1/territory/design

{
    "type": "object",
    "properties": {
        "count": {
            "type": "integer",
            "minimum": 1,
            "maximum": 200
        },
        "outline": {
            "type": [
                "boolean",
                "null"
            ]
        },
        "points": {
            "type": "array",
            "minItems": 1,
            "maxItems": 10000,
            "items": {
                "type": "object",
                "properties": {
                    "allowed_zones": {
                        "type": "array",
                        "maxItems": 200,
                        "items": {
                            "type": "string",
                            "maxLength": 128
                        }
                    },
                    "id": {
                        "type": "string",
                        "maxLength": 128
                    },
                    "lat": {
                        "type": "number",
                        "minimum": -90,
                        "maximum": 90
                    },
                    "lng": {
                        "type": "number",
                        "minimum": -180,
                        "maximum": 180
                    },
                    "weight": {
                        "type": [
                            "integer",
                            "null"
                        ],
                        "minimum": 1,
                        "maximum": 1000000
                    },
                    "zone": {
                        "type": [
                            "string",
                            "null"
                        ],
                        "maxLength": 128
                    }
                },
                "required": [
                    "id",
                    "lat",
                    "lng"
                ]
            }
        },
        "tolerance_percent": {
            "type": [
                "number",
                "null"
            ],
            "minimum": 0,
            "maximum": 50
        },
        "zones": {
            "type": "array",
            "minItems": 1,
            "maxItems": 200,
            "items": {
                "type": "object",
                "properties": {
                    "anchor": {
                        "type": [
                            "object",
                            "null"
                        ],
                        "properties": {
                            "lat": {
                                "type": "number",
                                "minimum": -90,
                                "maximum": 90,
                                "description": "Required when zones.*.anchor is present."
                            },
                            "lng": {
                                "type": "number",
                                "minimum": -180,
                                "maximum": 180,
                                "description": "Required when zones.*.anchor is present."
                            }
                        }
                    },
                    "id": {
                        "type": "string",
                        "maxLength": 128
                    },
                    "share": {
                        "type": [
                            "number",
                            "null"
                        ],
                        "maximum": 1000,
                        "exclusiveMinimum": 0
                    }
                },
                "required": [
                    "id"
                ]
            }
        }
    },
    "additionalProperties": false,
    "required": [
        "points",
        "zones"
    ]
}

Request example

{
    "points": [
        {
            "id": "stop-1",
            "lat": 45.48886,
            "lng": -73.63285,
            "weight": 1
        },
        {
            "id": "stop-2",
            "lat": 45.45869,
            "lng": -73.56354,
            "weight": 3
        },
        {
            "id": "stop-3",
            "lat": 45.51993,
            "lng": -73.49625,
            "weight": 2
        },
        {
            "id": "stop-4",
            "lat": 45.4545,
            "lng": -73.58194,
            "weight": 1
        },
        {
            "id": "stop-5",
            "lat": 45.47888,
            "lng": -73.56081,
            "weight": 1
        },
        {
            "id": "stop-6",
            "lat": 45.54922,
            "lng": -73.63772,
            "weight": 2
        },
        {
            "id": "stop-7",
            "lat": 45.52568,
            "lng": -73.55506,
            "weight": 1
        },
        {
            "id": "stop-8",
            "lat": 45.51925,
            "lng": -73.5886,
            "weight": 2
        },
        {
            "id": "stop-9",
            "lat": 45.45559,
            "lng": -73.50548,
            "weight": 3
        },
        {
            "id": "stop-10",
            "lat": 45.5003,
            "lng": -73.56268,
            "weight": 3
        },
        {
            "id": "stop-11",
            "lat": 45.51723,
            "lng": -73.53724,
            "weight": 1
        },
        {
            "id": "stop-12",
            "lat": 45.51979,
            "lng": -73.545,
            "weight": 3
        }
    ],
    "zones": [
        {
            "id": "east",
            "anchor": {
                "lat": 45.5,
                "lng": -73.55
            }
        },
        {
            "id": "west",
            "anchor": {
                "lat": 45.5,
                "lng": -73.64
            }
        },
        {
            "id": "north",
            "share": 0.5
        }
    ],
    "tolerance_percent": 20
}

Response

What a successful call returns: the schema of `data` and `meta`, with a description of each field, followed by an example response from a real call.

POST /v1/territory/design

Response schema

{
    "description": "The territories with their centres, weights and outlines, and the territory of every point. Billed per point.",
    "data": {
        "type": "object",
        "properties": {
            "zones": {
                "type": "array",
                "description": "The territories, in the order asked for.",
                "items": {
                    "type": "object",
                    "properties": {
                        "id": {
                            "type": "string",
                            "description": "The territory id: as given in `zones`, or 1..count."
                        },
                        "center": {
                            "type": [
                                "object",
                                "null"
                            ],
                            "description": "The territory's centre, `{lat, lng}`. Null for a territory that received no point."
                        },
                        "anchored": {
                            "type": "boolean",
                            "description": "Whether the territory grew around its `anchor`."
                        },
                        "points": {
                            "type": "integer",
                            "description": "Points in the territory."
                        },
                        "weight": {
                            "type": "integer",
                            "description": "The weight of its points."
                        },
                        "target": {
                            "type": "integer",
                            "description": "The weight it should carry: its share of the total."
                        },
                        "outline": {
                            "type": [
                                "object",
                                "null"
                            ],
                            "description": "The convex outline of its points as a GeoJSON Polygon ([lng, lat]). Null below three distinct points; absent when `outline` is false."
                        }
                    }
                }
            },
            "assignment": {
                "type": "array",
                "description": "Every point with its territory, in the order of `points`.",
                "items": {
                    "type": "object",
                    "properties": {
                        "point": {
                            "type": "string",
                            "description": "The point id."
                        },
                        "zone": {
                            "type": [
                                "string",
                                "null"
                            ],
                            "description": "The territory id; null when the point fits none."
                        }
                    }
                }
            },
            "unplaced": {
                "type": "array",
                "items": {
                    "type": "string"
                },
                "description": "Points that fit no territory (their `allowed_zones` are full)."
            },
            "summary": {
                "type": "object",
                "properties": {
                    "zones": {
                        "type": "integer",
                        "description": "Territories."
                    },
                    "points": {
                        "type": "integer",
                        "description": "Points submitted."
                    },
                    "weight": {
                        "type": "integer",
                        "description": "Total weight."
                    },
                    "largest_deviation_percent": {
                        "type": "number",
                        "description": "The largest difference between a territory's weight and its target, as a percentage of the target."
                    }
                }
            }
        },
        "required": [
            "zones",
            "assignment",
            "unplaced",
            "summary"
        ]
    },
    "meta": {
        "type": "object",
        "properties": {},
        "additionalProperties": true
    }
}

Response example

{
    "request_id": "req_01k8zq3m5n7p9r1t3v5x7z9b2d",
    "capability": "territory.design",
    "billing": {
        "unit": "point_1k",
        "quantity": 12,
        "counted": true
    },
    "data": {
        "zones": [
            {
                "id": "east",
                "center": {
                    "lat": 45.520306,
                    "lng": -73.5314
                },
                "anchored": true,
                "points": 4,
                "weight": 7,
                "target": 9,
                "outline": {
                    "type": "Polygon",
                    "coordinates": [
                        [
                            [
                                -73.55506,
                                45.52568
                            ],
                            [
                                -73.545,
                                45.51979
                            ],
                            [
                                -73.53724,
                                45.51723
                            ],
                            [
                                -73.49625,
                                45.51993
                            ],
                            [
                                -73.55506,
                                45.52568
                            ]
                        ]
                    ]
                }
            },
            {
                "id": "west",
                "center": {
                    "lat": 45.506008,
                    "lng": -73.591628
                },
                "anchored": true,
                "points": 6,
                "weight": 10,
                "target": 9,
                "outline": {
                    "type": "Polygon",
                    "coordinates": [
                        [
                            [
                                -73.63772,
                                45.54922
                            ],
                            [
                                -73.63285,
                                45.48886
                            ],
                            [
                                -73.58194,
                                45.4545
                            ],
                            [
                                -73.56081,
                                45.47888
                            ],
                            [
                                -73.56268,
                                45.5003
                            ],
                            [
                                -73.5886,
                                45.51925
                            ],
                            [
                                -73.63772,
                                45.54922
                            ]
                        ]
                    ]
                }
            },
            {
                "id": "north",
                "center": {
                    "lat": 45.45714,
                    "lng": -73.53451
                },
                "anchored": false,
                "points": 2,
                "weight": 6,
                "target": 5,
                "outline": null
            }
        ],
        "assignment": [
            {
                "point": "stop-1",
                "zone": "west"
            },
            {
                "point": "stop-2",
                "zone": "north"
            },
            {
                "point": "stop-3",
                "zone": "east"
            },
            {
                "point": "stop-4",
                "zone": "west"
            },
            {
                "point": "stop-5",
                "zone": "west"
            },
            {
                "point": "stop-6",
                "zone": "west"
            },
            {
                "point": "stop-7",
                "zone": "east"
            },
            {
                "point": "stop-8",
                "zone": "west"
            },
            {
                "point": "stop-9",
                "zone": "north"
            },
            {
                "point": "stop-10",
                "zone": "west"
            },
            {
                "point": "stop-11",
                "zone": "east"
            },
            {
                "point": "stop-12",
                "zone": "east"
            }
        ],
        "unplaced": [],
        "summary": {
            "zones": 3,
            "points": 12,
            "weight": 23,
            "largest_deviation_percent": 22.2
        }
    },
    "meta": {
        "distance": "straight_line",
        "rounds": 3,
        "mode": "sync"
    }
}

Example response when queued (202)

{
    "request_id": "req_01k8zq3m5n7p9r1t3v5x7z9b2d",
    "capability": "packing.solve",
    "billing": {
        "unit": "job",
        "quantity": 1,
        "counted": true,
        "breakdown": {
            "packagings": 2,
            "item_rows": 2,
            "units": 5
        }
    },
    "data": {
        "job": {
            "id": "job_01k8zq4a6c8e0g2j4l6n8q0s2u",
            "status": "queued",
            "capability": "packing.solve",
            "poll_after_ms": 1000,
            "result_url": "/v1/jobs/job_01k8zq4a6c8e0g2j4l6n8q0s2u/result",
            "cancel_url": "/v1/jobs/job_01k8zq4a6c8e0g2j4l6n8q0s2u/cancel"
        }
    },
    "meta": {
        "mode": "async"
    }
}

What it costs

Thousands of points, charged fractionally.

Every billable response carries a billing block with the unit, the quantity and how the quantity was derived.

Errors it can return

Status Code Description
401 TOKEN_INVALID Unknown or revoked API token.
401 TOKEN_MISSING No API token. Send Authorization: Bearer <your token>.
402 QUOTA_EXHAUSTED The included allowance for this capability is used up for the current period.
403 CAPABILITY_NOT_ENABLED This capability is not enabled for your account. See :doc to enable it.
403 CAPABILITY_NOT_GRANTED_TO_KEY Your account has this capability, but this API key was not granted it.
403 OPEN_API_DISABLED Open Platform access is not enabled for this account.
413 REQUEST_TOO_LARGE The request body is too large. Upload large payloads as a reusable resource instead.
422 CALLBACK_NOT_VERIFIED Verify the callback endpoint first: POST /v1/webhooks/verify and echo the challenge back.
422 CALLBACK_URL_UNSAFE The callback URL must be a public HTTPS endpoint.
422 VALIDATION_FAILED The request failed validation. See details.
429 CONCURRENCY_LIMIT Too many solves are already running for this account. Retry when one finishes.
429 RATE_LIMITED Too many requests. Please retry after the indicated delay.
500 INTERNAL_ERROR An unexpected error occurred. Quote the request_id when contacting support.