---
title: Subcontractors
description: The Subcontractors API manages the suppliers a part can be bought from: create, list, update, and delete them, and suppress or restore individual presets.
---

# Subcontractors

The Subcontractors API manages the suppliers a part can be bought from: create, list, update, and delete them, and suppress or restore individual presets.

> **Auto-generated** from the public OpenAPI spec — this page never
> drifts from the running API. Base URL `https://api.arcnm.io`. Authenticate with
> the `X-API-Key` header (see [Authentication](../authentication.md)).

## List Subcontractors

`GET /api/v1/subcontractors`

Your organization's subcontractors, name order — the platform
presets (hardening, plating, galvanizing, anodizing, painting,
marking; ``preset_key`` set) and your own.

**Request**

<CodeTabs>

```bash title="cURL"
curl -X GET https://api.arcnm.io/api/v1/subcontractors \
  -H "X-API-Key: $ARCNM_API_KEY"
```

```python title="Python"
import requests

resp = requests.get(
    "https://api.arcnm.io/api/v1/subcontractors",
    headers={"X-API-Key": "YOUR_API_KEY"},
)
resp.raise_for_status()
print(resp.json())
```

```typescript title="TypeScript"
const resp = await fetch("https://api.arcnm.io/api/v1/subcontractors", {
  method: "GET",
  headers: {
    "X-API-Key": process.env.ARCNM_API_KEY!,
  },
})
const data = await resp.json()
```

</CodeTabs>

**Responses**

| Status | Description |
| --- | --- |
| `200` | Successful Response |
| `422` | Validation Error |

**Errors**

Standard error responses — see the [Errors catalog](../errors.md) for the full envelope, `request_id`, and retry-safety table.

| Status | Code | When |
| --- | --- | --- |
| `401` | `invalid_api_key` | Missing, malformed, or revoked API key. |
| `403` | `insufficient_scope` | The key is valid but lacks a scope this endpoint requires. |
| `429` | `rate_limited` | Per-key or per-org rate limit exceeded — back off with jitter and retry. |


## Create Subcontractor

`POST /api/v1/subcontractors`

Add a subcontractor to your organization.

``lead_time_days`` is stored for planning surfaces; prices are
unaffected. Capabilities say which external operations this shop
offers — a costing environment that prices an operation none of its
member subcontractors offer carries a ``subcontract_unsourced`` note
on the result (never a veto).

**Request body** (`application/json`)

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `capabilities` | SubcontractorCapability[] | no |  |
| `is_active` | boolean | no | Inactive subcontractors keep their history but stop counting as a source. |
| `lead_time_days` | integer | no | Typical door-to-door lead time. Stored for planning surfaces; prices are unaffected. |
| `name` | string | yes | The shop you order from. |
| `notes` | string | no |  |
| `region` | string | no | Region / city, free text. |

**Request**

<CodeTabs>

```bash title="cURL"
curl -X POST https://api.arcnm.io/api/v1/subcontractors \
  -H "X-API-Key: $ARCNM_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "string"
  }'
```

```python title="Python"
import requests

resp = requests.post(
    "https://api.arcnm.io/api/v1/subcontractors",
    headers={"X-API-Key": "YOUR_API_KEY"},
    json={
        "name": "string"
    },
)
resp.raise_for_status()
print(resp.json())
```

```typescript title="TypeScript"
const resp = await fetch("https://api.arcnm.io/api/v1/subcontractors", {
  method: "POST",
  headers: {
    "X-API-Key": process.env.ARCNM_API_KEY!,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    "name": "string"
  }),
})
const data = await resp.json()
```

</CodeTabs>

**Responses**

| Status | Description |
| --- | --- |
| `201` | Successful Response |
| `422` | Validation Error |

**Errors**

Standard error responses — see the [Errors catalog](../errors.md) for the full envelope, `request_id`, and retry-safety table.

| Status | Code | When |
| --- | --- | --- |
| `401` | `invalid_api_key` | Missing, malformed, or revoked API key. |
| `403` | `insufficient_scope` | The key is valid but lacks a scope this endpoint requires. |
| `409` | `conflict` | A conflicting change, or an `Idempotency-Key` reused with a different body. |
| `429` | `rate_limited` | Per-key or per-org rate limit exceeded — back off with jitter and retry. |


**Response body** `201`

| Field | Type | Description |
| --- | --- | --- |
| `capabilities` | SubcontractorCapability[] |  |
| `created_at` | string |  |
| `hidden` | boolean |  |
| `id` | string |  |
| `is_active` | boolean | Inactive subcontractors keep their history but stop counting as a source. |
| `lead_time_days` | integer | Typical door-to-door lead time. Stored for planning surfaces; prices are unaffected. |
| `name` | string | The shop you order from. |
| `notes` | string |  |
| `preset_key` | string |  |
| `region` | string | Region / city, free text. |
| `updated_at` | string |  |

**Example response**

```json
{
  "capabilities": [
    {
      "lot_minimum_eur": 0,
      "operation_kind": "string",
      "order_fee_eur": 0,
      "pricing_unit": "string"
    }
  ],
  "created_at": "2026-06-01T12:00:00Z",
  "hidden": false,
  "id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
  "is_active": true,
  "lead_time_days": 0,
  "name": "string",
  "notes": "string",
  "preset_key": "string",
  "region": "EU",
  "updated_at": "2026-06-01T12:00:00Z"
}
```

## Delete Subcontractor

`DELETE /api/v1/subcontractors/{sub_id}`

Remove a subcontractor. Its environment memberships go with it;
rate rows that named it keep their price and lose only the name
(provenance FK is SET NULL — a price you entered stays a price).

**Parameters**

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `sub_id` | path | string | yes | Identifier of the sub. |

**Request**

<CodeTabs>

```bash title="cURL"
curl -X DELETE https://api.arcnm.io/api/v1/subcontractors/{sub_id} \
  -H "X-API-Key: $ARCNM_API_KEY"
```

```python title="Python"
import requests

resp = requests.delete(
    "https://api.arcnm.io/api/v1/subcontractors/{sub_id}",
    headers={"X-API-Key": "YOUR_API_KEY"},
)
resp.raise_for_status()
print(resp.json())
```

```typescript title="TypeScript"
const resp = await fetch("https://api.arcnm.io/api/v1/subcontractors/{sub_id}", {
  method: "DELETE",
  headers: {
    "X-API-Key": process.env.ARCNM_API_KEY!,
  },
})
const data = await resp.json()
```

</CodeTabs>

**Responses**

| Status | Description |
| --- | --- |
| `200` | Successful Response |
| `422` | Validation Error |

**Errors**

Standard error responses — see the [Errors catalog](../errors.md) for the full envelope, `request_id`, and retry-safety table.

| Status | Code | When |
| --- | --- | --- |
| `401` | `invalid_api_key` | Missing, malformed, or revoked API key. |
| `403` | `insufficient_scope` | The key is valid but lacks a scope this endpoint requires. |
| `404` | `not_found` | A referenced resource doesn't exist or isn't visible to your organisation. |
| `409` | `conflict` | A conflicting change, or an `Idempotency-Key` reused with a different body. |
| `429` | `rate_limited` | Per-key or per-org rate limit exceeded — back off with jitter and retry. |


**Response body** `200`

| Field | Type | Description |
| --- | --- | --- |
| `message` | string | Human-readable result of the operation. |

**Example response**

```json
{
  "message": "string"
}
```

## Update Subcontractor

`PATCH /api/v1/subcontractors/{sub_id}`

Edit a subcontractor's identity, lead time, or capabilities.

**Parameters**

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `sub_id` | path | string | yes | Identifier of the sub. |

**Request body** (`application/json`)

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `capabilities` | SubcontractorCapability[] | no |  |
| `is_active` | boolean | no |  |
| `lead_time_days` | integer | no |  |
| `name` | string | no |  |
| `notes` | string | no |  |
| `region` | string | no |  |

**Request**

<CodeTabs>

```bash title="cURL"
curl -X PATCH https://api.arcnm.io/api/v1/subcontractors/{sub_id} \
  -H "X-API-Key: $ARCNM_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "capabilities": [
      {
        "lot_minimum_eur": 0,
        "operation_kind": "string",
        "order_fee_eur": 0,
        "pricing_unit": "string"
      }
    ],
    "is_active": true,
    "lead_time_days": 0,
    "name": "string",
    "notes": "string",
    "region": "EU"
  }'
```

```python title="Python"
import requests

resp = requests.patch(
    "https://api.arcnm.io/api/v1/subcontractors/{sub_id}",
    headers={"X-API-Key": "YOUR_API_KEY"},
    json={
        "capabilities": [
            {
                "lot_minimum_eur": 0,
                "operation_kind": "string",
                "order_fee_eur": 0,
                "pricing_unit": "string"
            }
        ],
        "is_active": True,
        "lead_time_days": 0,
        "name": "string",
        "notes": "string",
        "region": "EU"
    },
)
resp.raise_for_status()
print(resp.json())
```

```typescript title="TypeScript"
const resp = await fetch("https://api.arcnm.io/api/v1/subcontractors/{sub_id}", {
  method: "PATCH",
  headers: {
    "X-API-Key": process.env.ARCNM_API_KEY!,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    "capabilities": [
      {
        "lot_minimum_eur": 0,
        "operation_kind": "string",
        "order_fee_eur": 0,
        "pricing_unit": "string"
      }
    ],
    "is_active": true,
    "lead_time_days": 0,
    "name": "string",
    "notes": "string",
    "region": "EU"
  }),
})
const data = await resp.json()
```

</CodeTabs>

**Responses**

| Status | Description |
| --- | --- |
| `200` | Successful Response |
| `422` | Validation Error |

**Errors**

Standard error responses — see the [Errors catalog](../errors.md) for the full envelope, `request_id`, and retry-safety table.

| Status | Code | When |
| --- | --- | --- |
| `401` | `invalid_api_key` | Missing, malformed, or revoked API key. |
| `403` | `insufficient_scope` | The key is valid but lacks a scope this endpoint requires. |
| `404` | `not_found` | A referenced resource doesn't exist or isn't visible to your organisation. |
| `409` | `conflict` | A conflicting change, or an `Idempotency-Key` reused with a different body. |
| `429` | `rate_limited` | Per-key or per-org rate limit exceeded — back off with jitter and retry. |


**Response body** `200`

| Field | Type | Description |
| --- | --- | --- |
| `capabilities` | SubcontractorCapability[] |  |
| `created_at` | string |  |
| `hidden` | boolean |  |
| `id` | string |  |
| `is_active` | boolean | Inactive subcontractors keep their history but stop counting as a source. |
| `lead_time_days` | integer | Typical door-to-door lead time. Stored for planning surfaces; prices are unaffected. |
| `name` | string | The shop you order from. |
| `notes` | string |  |
| `preset_key` | string |  |
| `region` | string | Region / city, free text. |
| `updated_at` | string |  |

**Example response**

```json
{
  "capabilities": [
    {
      "lot_minimum_eur": 0,
      "operation_kind": "string",
      "order_fee_eur": 0,
      "pricing_unit": "string"
    }
  ],
  "created_at": "2026-06-01T12:00:00Z",
  "hidden": false,
  "id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
  "is_active": true,
  "lead_time_days": 0,
  "name": "string",
  "notes": "string",
  "preset_key": "string",
  "region": "EU",
  "updated_at": "2026-06-01T12:00:00Z"
}
```

## Unsuppress Subcontractor Preset

`DELETE /api/v1/subcontractors/presets/{preset_key}/suppression`

Show a hidden preset again — it rejoins the default environment
(a new membership row; the closed one keeps its window).

**Parameters**

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `preset_key` | path | string | yes |  |

**Request**

<CodeTabs>

```bash title="cURL"
curl -X DELETE https://api.arcnm.io/api/v1/subcontractors/presets/{preset_key}/suppression \
  -H "X-API-Key: $ARCNM_API_KEY"
```

```python title="Python"
import requests

resp = requests.delete(
    "https://api.arcnm.io/api/v1/subcontractors/presets/{preset_key}/suppression",
    headers={"X-API-Key": "YOUR_API_KEY"},
)
resp.raise_for_status()
print(resp.json())
```

```typescript title="TypeScript"
const resp = await fetch("https://api.arcnm.io/api/v1/subcontractors/presets/{preset_key}/suppression", {
  method: "DELETE",
  headers: {
    "X-API-Key": process.env.ARCNM_API_KEY!,
  },
})
const data = await resp.json()
```

</CodeTabs>

**Responses**

| Status | Description |
| --- | --- |
| `200` | Successful Response |
| `422` | Validation Error |

**Errors**

Standard error responses — see the [Errors catalog](../errors.md) for the full envelope, `request_id`, and retry-safety table.

| Status | Code | When |
| --- | --- | --- |
| `401` | `invalid_api_key` | Missing, malformed, or revoked API key. |
| `403` | `insufficient_scope` | The key is valid but lacks a scope this endpoint requires. |
| `404` | `not_found` | A referenced resource doesn't exist or isn't visible to your organisation. |
| `409` | `conflict` | A conflicting change, or an `Idempotency-Key` reused with a different body. |
| `429` | `rate_limited` | Per-key or per-org rate limit exceeded — back off with jitter and retry. |


**Response body** `200`

| Field | Type | Description |
| --- | --- | --- |
| `message` | string | Human-readable result of the operation. |

**Example response**

```json
{
  "message": "string"
}
```

## Suppress Subcontractor Preset

`POST /api/v1/subcontractors/presets/{preset_key}/suppression`

Hide a platform subcontractor preset ("I don't buy this in"): it
leaves the default environment (SCD2) and stays out of it until shown
again or the platform set is restored. Your own subcontractors are
deleted, not hidden.

**Parameters**

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `preset_key` | path | string | yes |  |

**Request body** (`application/json`)

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `reason` | string | no |  |

**Request**

<CodeTabs>

```bash title="cURL"
curl -X POST https://api.arcnm.io/api/v1/subcontractors/presets/{preset_key}/suppression \
  -H "X-API-Key: $ARCNM_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "reason": "string"
  }'
```

```python title="Python"
import requests

resp = requests.post(
    "https://api.arcnm.io/api/v1/subcontractors/presets/{preset_key}/suppression",
    headers={"X-API-Key": "YOUR_API_KEY"},
    json={
        "reason": "string"
    },
)
resp.raise_for_status()
print(resp.json())
```

```typescript title="TypeScript"
const resp = await fetch("https://api.arcnm.io/api/v1/subcontractors/presets/{preset_key}/suppression", {
  method: "POST",
  headers: {
    "X-API-Key": process.env.ARCNM_API_KEY!,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    "reason": "string"
  }),
})
const data = await resp.json()
```

</CodeTabs>

**Responses**

| Status | Description |
| --- | --- |
| `201` | Successful Response |
| `422` | Validation Error |

**Errors**

Standard error responses — see the [Errors catalog](../errors.md) for the full envelope, `request_id`, and retry-safety table.

| Status | Code | When |
| --- | --- | --- |
| `401` | `invalid_api_key` | Missing, malformed, or revoked API key. |
| `403` | `insufficient_scope` | The key is valid but lacks a scope this endpoint requires. |
| `404` | `not_found` | A referenced resource doesn't exist or isn't visible to your organisation. |
| `409` | `conflict` | A conflicting change, or an `Idempotency-Key` reused with a different body. |
| `429` | `rate_limited` | Per-key or per-org rate limit exceeded — back off with jitter and retry. |


**Response body** `201`

| Field | Type | Description |
| --- | --- | --- |
| `message` | string | Human-readable result of the operation. |

**Example response**

```json
{
  "message": "string"
}
```
