# Register specials targets

Idempotent, up to 200 sites per call. Each site is matched by exact normalized host; an unknown
host becomes a new site. Creates the `(site, kind)` collection target if it does not exist and
never changes an existing one. New targets start in shadow (collected and diffed, not
published) until the site passes parity and is promoted. Records the agency's entitlement, so the
credential can read the site's specials, history and runs. `seed_urls` (known specials pages,
on the site's host) and `group_hosts` (sister-store hosts whose images count as dealer-owned)
are merged with earlier registrations. Kinds `media`, `site_pages` and `ad_library` are planned
(`400 kind_not_available`). Needs scope `v2:write`. Returns 201 when anything was created.

## Endpoint

```
POST https://api.motorsdynamic.com/api/v2/targets
```

## Plan and price

| Plan | Access | Price |
| --- | --- | --- |
| Free | No | not included |
| Growth | No | not included |
| Scale | No | not included |
| Enterprise | Yes | contract |

A call is billed when it answers 2xx. See [pricing](https://developers.motorsdynamic.com/pricing.md).

## Request body

| Field | Type | Description |
| --- | --- | --- |
| `agency_id` | integer | Required only when the credential can act for more than one agency. |
| `domains[]` | array of object |  |
| `domains[].url` | string |  |
| `domains[].name` | string |  |
| `domains[].kinds[]` | array of specials |  |
| `domains[].seed_urls[]` | array of string |  |
| `domains[].group_hosts[]` | array of string |  |
| `domains[].cms_hint` | string |  |

## Example request

curl:

```bash
curl -X POST "https://api.motorsdynamic.com/api/v2/targets" \
  -H "x-api-key: $MD_API_KEY" \
  -H "content-type: application/json" \
  -d '{"domains":[{"url":"https://www.newkiastore.com/","seed_urls":["https://www.newkiastore.com/new-specials/"],"group_hosts":["sisterkia.com"]}]}'
```

JavaScript:

```javascript
const res = await fetch("https://api.motorsdynamic.com/api/v2/targets", {
  method: "POST",
  headers: { "x-api-key": process.env.MD_API_KEY, "content-type": "application/json" },
  body: JSON.stringify({"domains":[{"url":"https://www.newkiastore.com/","seed_urls":["https://www.newkiastore.com/new-specials/"],"group_hosts":["sisterkia.com"]}]}),
});
if (!res.ok) throw new Error(`${res.status} ${(await res.json()).error}`);
const data = await res.json();
console.log(data);
```

Python:

```python
import os, requests

res = requests.post(
    "https://api.motorsdynamic.com/api/v2/targets",
    headers={"x-api-key": os.environ["MD_API_KEY"]},
    json={"domains":[{"url":"https://www.newkiastore.com/","seed_urls":["https://www.newkiastore.com/new-specials/"],"group_hosts":["sisterkia.com"]}]},
    timeout=10,
)
res.raise_for_status()
print(res.json())
```

## Example response

```json
{
  "data": [
    {
      "domain_id": 1000000123,
      "host": "newkiastore.com",
      "created": {
        "site": true,
        "targets": [
          "specials"
        ],
        "entitlement": true
      },
      "targets": [
        {
          "kind": "specials",
          "mode": "shadow",
          "next_crawl_at": "2026-09-24T10:00:00.000000Z",
          "last_complete_at": null
        }
      ]
    }
  ],
  "meta": {
    "agency_id": 2
  }
}
```

## Response fields

| Field | Type | Description |
| --- | --- | --- |
| `data[]` | array of object |  |
| `data[].domain_id` | integer |  |
| `data[].host` | string |  |
| `data[].created` | object |  |
| `data[].created.site` | boolean |  |
| `data[].created.targets[]` | array of string |  |
| `data[].created.entitlement` | boolean |  |
| `data[].targets[]` | array of object |  |
| `data[].targets[].kind` | string |  |
| `data[].targets[].mode` | `shadow` or `publish` |  |
| `data[].targets[].next_crawl_at` | string or null | RFC 3339 UTC with microseconds. |
| `data[].targets[].last_complete_at` | string or null | RFC 3339 UTC with microseconds. |
| `meta` | object |  |

## Errors

| Status | Code | When |
| --- | --- | --- |
| 400 | `invalid_parameter` | A parameter is missing or malformed. The message names it. |
| 401 | `invalid_api_key` | The key is missing, revoked or mistyped. |
| 402 | `plan_required` | The endpoint is on a higher plan. The body names the plan and links to the upgrade. |
| 403 | `not_entitled` | The key may not read this dealer or source. |
| 429 | `rate_limited or quota_exceeded` | Slow down (see `Retry-After`), or the Free monthly calls are used up. |
| 504 | `query_timeout` | The query passed its 4 second budget. Narrow the filters. Not billed. |

Every error has the same body: `{ "error": "<code>", "message": "..." }`. Full list: [errors](https://developers.motorsdynamic.com/docs/errors.md).
