Skip to content

Developers

API documentation

How your systems read cables, families and files and create requests. The examples show made-up data of a sample site; your own address and data appear only with an API key.

Basics

Base URL: https://testing.cablecreations.de/api/v1

  • All responses are JSON. Timestamps are ISO 8601 in UTC; IDs are opaque strings.
  • The API returns only published cables and public files, the same content your public catalog shows.
  • File URLs are absolute and can be cached permanently: when a file changes, its contentHash changes.
  • The responses are a stable contract of version v1. New fields may be added; existing ones do not change their meaning.
  • The technical data of a cable (technical) is an ordered list: the order is that of the datasheet.

OpenAPI document (JSON)

Authentication

Every call needs an API key of your organization in the Authorization header:

Authorization: Bearer cbl_…
  • A key is valid for your organization only. It cannot read or change anything that belongs to another organization.
  • The Read permission is enough for all retrievals; to create requests, the key also needs Write.
  • Requests created through the API are attributed to the person who created the key. If that person leaves your organization, the key can still read but no longer write: create a new one.
  • The API is meant for server-to-server communication. It sends no CORS headers: never use the key in a browser or an app, only on your server.
  • Treat the key like a password. Revoke it as soon as it is no longer needed or might have been exposed.

Calls

List cables

GET /api/v1/cablesPermission: Read

All published cables of your organization, sorted by URL name (slug), with technical data and public files.

Request

curl "https://testing.cablecreations.de/api/v1/cables?limit=1" \
  -H "Authorization: Bearer $API_KEY"

Response (200)

{
  "data": [
    {
      "id": "0f8b3c1e-5a0d-4a51-9d0e-3a8f4a8f5b11",
      "slug": "nym-j-3x1-5",
      "name": "NYM-J 3x1,5",
      "designation": "NYM-J 3x1,5 mm²",
      "description": "Mantelleitung für die feste Verlegung.",
      "family": {
        "id": "7c1d2a90-0d53-4f0e-8f47-5a0c1c0a2e01",
        "slug": "installationsleitungen",
        "name": "Installationsleitungen"
      },
      "technical": [
        {
          "kind": "field",
          "label": "Nennspannung",
          "value": {
            "kind": "text",
            "text": "300/500 V"
          }
        },
        {
          "kind": "field",
          "label": "Aderzahl",
          "value": {
            "kind": "text",
            "text": "3"
          }
        }
      ],
      "assets": [
        {
          "id": "b6f0a4d2-93c4-4a6b-a1b5-2f1f6f3f9c22",
          "kind": "image",
          "name": "nym-j-3x1-5-catalog-profile.png",
          "url": "https://kabel.example/files/b6f0a4d2-93c4-4a6b-a1b5-2f1f6f3f9c22/nym-j-3x1-5-catalog-profile.png",
          "mime": "image/png",
          "bytes": 184320,
          "width": 1600,
          "height": 256,
          "contentHash": "9f2c7d0b6a1e4c3f8b5d2a7e0c9f4b1d6e3a8c5f2b7d0e9a4c1f6b3d8e5a2c7f",
          "createdAt": "2026-03-09T12:00:00.000Z"
        }
      ],
      "mainPictureId": "b6f0a4d2-93c4-4a6b-a1b5-2f1f6f3f9c22",
      "variants": [
        {
          "id": "3d0c7e52-6c1a-4a0e-9a57-0b6a4a3b7d10",
          "name": "3x1,5",
          "articleNumber": "100-315",
          "outerDiameterMm": 8.4,
          "weightKgPerKm": 122,
          "copperKgPerKm": 43,
          "extra": [
            {
              "label": "Biegeradius",
              "value": "34 mm"
            }
          ],
          "hasModel": true,
          "crossSectionUrl": "https://kabel.example/files/derived/7c2f1d7e-0b4a-4f0e-9d53-6a1b2c3d4e5f/4be1c07a92d35f68/querschnitt-nym-j-3x1-5.svg",
          "crossSectionDatasheetUrl": "https://kabel.example/files/derived/a41d9c3b-58e2-4c17-b0f6-2d7e8f9a0b1c/90c3e6f1a2b4d578/querschnitt-sw-nym-j-3x1-5.svg",
          "webModelUrl": "https://kabel.example/files/derived/e8b05f26-1c3d-4a79-8e42-5f6a7b8c9d0e/1f6d2a8c4e0b7359/modell-nym-j-3x1-5.glb",
          "crossSectionLineArtUrl": "https://kabel.example/files/derived/3d7a2c51-9e4b-4f86-a1c0-6b8e2d4f7a19/5e8c1a3f7b2d9046/querschnitt-linien-nym-j-3x1-5.svg",
          "crossSectionShadedUrl": "https://kabel.example/files/derived/9f41c2d8-6a7e-4b35-8d10-2e5c7b9a4f63/7a2e5c9d1f4b8036/querschnitt-schattiert-nym-j-3x1-5.svg",
          "crossSectionEngravedUrl": "https://kabel.example/files/derived/c58e3b1a-2d9f-4e70-b6a4-8f1c0d7e5a92/e9b4d27c3a6f1085/querschnitt-kreuzschraffur-nym-j-3x1-5.svg",
          "profileUrl": "https://kabel.example/files/derived/b62e9f17-4a3c-4d58-9b0e-1c7f3a5d8e24/c3a91f6e2b8d4075/profil-nym-j-3x1-5.svg"
        },
        {
          "id": "5a9e1f04-2b7c-4d3e-8c61-7e2f9d0a1b33",
          "name": "5x2,5",
          "articleNumber": null,
          "outerDiameterMm": 11.2,
          "weightKgPerKm": null,
          "copperKgPerKm": null,
          "extra": [],
          "hasModel": false,
          "crossSectionUrl": null,
          "crossSectionDatasheetUrl": null,
          "webModelUrl": null,
          "crossSectionLineArtUrl": null,
          "crossSectionShadedUrl": null,
          "crossSectionEngravedUrl": null,
          "profileUrl": null
        }
      ],
      "publishedAt": "2026-03-10T08:30:00.000Z",
      "updatedAt": "2026-03-10T08:30:00.000Z",
      "datasheetUrl": "https://kabel.example/api/v1/cables/nym-j-3x1-5/datasheet"
    }
  ],
  "next": "WyJueW0tai0zeDEtNSJd"
}

Retrieve a cable

GET /api/v1/cables/{slug}Permission: Read

One published cable by its URL name. Drafts and archived cables respond with 404.

Request

curl "https://testing.cablecreations.de/api/v1/cables/nym-j-3x1-5" \
  -H "Authorization: Bearer $API_KEY"

Response (200)

{
  "id": "0f8b3c1e-5a0d-4a51-9d0e-3a8f4a8f5b11",
  "slug": "nym-j-3x1-5",
  "name": "NYM-J 3x1,5",
  "designation": "NYM-J 3x1,5 mm²",
  "description": "Mantelleitung für die feste Verlegung.",
  "family": {
    "id": "7c1d2a90-0d53-4f0e-8f47-5a0c1c0a2e01",
    "slug": "installationsleitungen",
    "name": "Installationsleitungen"
  },
  "technical": [
    {
      "kind": "field",
      "label": "Nennspannung",
      "value": {
        "kind": "text",
        "text": "300/500 V"
      }
    },
    {
      "kind": "field",
      "label": "Aderzahl",
      "value": {
        "kind": "text",
        "text": "3"
      }
    }
  ],
  "assets": [
    {
      "id": "b6f0a4d2-93c4-4a6b-a1b5-2f1f6f3f9c22",
      "kind": "image",
      "name": "nym-j-3x1-5-catalog-profile.png",
      "url": "https://kabel.example/files/b6f0a4d2-93c4-4a6b-a1b5-2f1f6f3f9c22/nym-j-3x1-5-catalog-profile.png",
      "mime": "image/png",
      "bytes": 184320,
      "width": 1600,
      "height": 256,
      "contentHash": "9f2c7d0b6a1e4c3f8b5d2a7e0c9f4b1d6e3a8c5f2b7d0e9a4c1f6b3d8e5a2c7f",
      "createdAt": "2026-03-09T12:00:00.000Z"
    }
  ],
  "mainPictureId": "b6f0a4d2-93c4-4a6b-a1b5-2f1f6f3f9c22",
  "variants": [
    {
      "id": "3d0c7e52-6c1a-4a0e-9a57-0b6a4a3b7d10",
      "name": "3x1,5",
      "articleNumber": "100-315",
      "outerDiameterMm": 8.4,
      "weightKgPerKm": 122,
      "copperKgPerKm": 43,
      "extra": [
        {
          "label": "Biegeradius",
          "value": "34 mm"
        }
      ],
      "hasModel": true,
      "crossSectionUrl": "https://kabel.example/files/derived/7c2f1d7e-0b4a-4f0e-9d53-6a1b2c3d4e5f/4be1c07a92d35f68/querschnitt-nym-j-3x1-5.svg",
      "crossSectionDatasheetUrl": "https://kabel.example/files/derived/a41d9c3b-58e2-4c17-b0f6-2d7e8f9a0b1c/90c3e6f1a2b4d578/querschnitt-sw-nym-j-3x1-5.svg",
      "webModelUrl": "https://kabel.example/files/derived/e8b05f26-1c3d-4a79-8e42-5f6a7b8c9d0e/1f6d2a8c4e0b7359/modell-nym-j-3x1-5.glb",
      "crossSectionLineArtUrl": "https://kabel.example/files/derived/3d7a2c51-9e4b-4f86-a1c0-6b8e2d4f7a19/5e8c1a3f7b2d9046/querschnitt-linien-nym-j-3x1-5.svg",
      "crossSectionShadedUrl": "https://kabel.example/files/derived/9f41c2d8-6a7e-4b35-8d10-2e5c7b9a4f63/7a2e5c9d1f4b8036/querschnitt-schattiert-nym-j-3x1-5.svg",
      "crossSectionEngravedUrl": "https://kabel.example/files/derived/c58e3b1a-2d9f-4e70-b6a4-8f1c0d7e5a92/e9b4d27c3a6f1085/querschnitt-kreuzschraffur-nym-j-3x1-5.svg",
      "profileUrl": "https://kabel.example/files/derived/b62e9f17-4a3c-4d58-9b0e-1c7f3a5d8e24/c3a91f6e2b8d4075/profil-nym-j-3x1-5.svg"
    },
    {
      "id": "5a9e1f04-2b7c-4d3e-8c61-7e2f9d0a1b33",
      "name": "5x2,5",
      "articleNumber": null,
      "outerDiameterMm": 11.2,
      "weightKgPerKm": null,
      "copperKgPerKm": null,
      "extra": [],
      "hasModel": false,
      "crossSectionUrl": null,
      "crossSectionDatasheetUrl": null,
      "webModelUrl": null,
      "crossSectionLineArtUrl": null,
      "crossSectionShadedUrl": null,
      "crossSectionEngravedUrl": null,
      "profileUrl": null
    }
  ],
  "publishedAt": "2026-03-10T08:30:00.000Z",
  "updatedAt": "2026-03-10T08:30:00.000Z",
  "datasheetUrl": "https://kabel.example/api/v1/cables/nym-j-3x1-5/datasheet"
}

List files

GET /api/v1/assetsPermission: Read

The public files of your published cables, newest first. Use cable=<slug> to get the files of one cable only.

Request

curl "https://testing.cablecreations.de/api/v1/assets?cable=nym-j-3x1-5" \
  -H "Authorization: Bearer $API_KEY"

Response (200)

{
  "data": [
    {
      "id": "b6f0a4d2-93c4-4a6b-a1b5-2f1f6f3f9c22",
      "kind": "image",
      "name": "nym-j-3x1-5-catalog-profile.png",
      "url": "https://kabel.example/files/b6f0a4d2-93c4-4a6b-a1b5-2f1f6f3f9c22/nym-j-3x1-5-catalog-profile.png",
      "mime": "image/png",
      "bytes": 184320,
      "width": 1600,
      "height": 256,
      "contentHash": "9f2c7d0b6a1e4c3f8b5d2a7e0c9f4b1d6e3a8c5f2b7d0e9a4c1f6b3d8e5a2c7f",
      "createdAt": "2026-03-09T12:00:00.000Z",
      "cable": {
        "slug": "nym-j-3x1-5",
        "name": "NYM-J 3x1,5"
      }
    }
  ],
  "next": null
}

List families

GET /api/v1/familiesPermission: Read

The cable families that contain at least one published cable, sorted by position.

Request

curl "https://testing.cablecreations.de/api/v1/families" \
  -H "Authorization: Bearer $API_KEY"

Response (200)

{
  "data": [
    {
      "id": "7c1d2a90-0d53-4f0e-8f47-5a0c1c0a2e01",
      "slug": "installationsleitungen",
      "name": "Installationsleitungen",
      "position": 1,
      "cableCount": 12
    }
  ],
  "next": null
}

Create a request

POST /api/v1/requestsPermission: Write

Creates a request for a new cable. Only the title is required; the same rules apply as in the portal’s form. Unknown fields are rejected.

Request

curl -X POST "https://testing.cablecreations.de/api/v1/requests" \
  -H "Authorization: Bearer $API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"title":"Steuerleitung für Schleppketten","construction":"4x2x0,75","sheathColour":"grau RAL 7001","datasheetReference":"https://example.com/datenblatt.pdf"}'

Response (201)

{
  "id": "3d5e7a10-2b4c-4d6e-8f90-1a2b3c4d5e6f",
  "title": "Steuerleitung für Schleppketten",
  "status": "open",
  "details": {
    "designation": null,
    "construction": "4x2x0,75",
    "sheathColour": "grau RAL 7001",
    "printText": null,
    "datasheetReference": "https://example.com/datenblatt.pdf",
    "note": null
  },
  "source": "api",
  "rejectionReason": null,
  "cable": null,
  "createdAt": "2026-04-01T10:00:00.000Z",
  "updatedAt": "2026-04-01T10:00:00.000Z"
}

List requests

GET /api/v1/requestsPermission: Read

The requests of your organization, newest first.

Request

curl "https://testing.cablecreations.de/api/v1/requests" \
  -H "Authorization: Bearer $API_KEY"

Response (200)

{
  "data": [
    {
      "id": "3d5e7a10-2b4c-4d6e-8f90-1a2b3c4d5e6f",
      "title": "Steuerleitung für Schleppketten",
      "status": "open",
      "details": {
        "designation": null,
        "construction": "4x2x0,75",
        "sheathColour": "grau RAL 7001",
        "printText": null,
        "datasheetReference": "https://example.com/datenblatt.pdf",
        "note": null
      },
      "source": "api",
      "rejectionReason": null,
      "cable": null,
      "createdAt": "2026-04-01T10:00:00.000Z",
      "updatedAt": "2026-04-01T10:00:00.000Z"
    }
  ],
  "next": null
}

Retrieve a request

GET /api/v1/requests/{id}Permission: Read

The status of a request. A rejected request gives the reason in rejectionReason; a delivered one gives the cable in cable as soon as it is published.

Request

curl "https://testing.cablecreations.de/api/v1/requests/3d5e7a10-2b4c-4d6e-8f90-1a2b3c4d5e6f" \
  -H "Authorization: Bearer $API_KEY"

Response (200)

{
  "id": "3d5e7a10-2b4c-4d6e-8f90-1a2b3c4d5e6f",
  "title": "Steuerleitung für Schleppketten",
  "status": "open",
  "details": {
    "designation": null,
    "construction": "4x2x0,75",
    "sheathColour": "grau RAL 7001",
    "printText": null,
    "datasheetReference": "https://example.com/datenblatt.pdf",
    "note": null
  },
  "source": "api",
  "rejectionReason": null,
  "cable": null,
  "createdAt": "2026-04-01T10:00:00.000Z",
  "updatedAt": "2026-04-01T10:00:00.000Z"
}

Retrieve motifs and prices

GET /api/v1/render-motifsPermission: Order

All motifs you can order, with sizes in pixels and the price per size, with and without a surface. The price of an order (amount) gives it in euros. options lists the file formats and the surfaces.

Request

curl "https://testing.cablecreations.de/api/v1/render-motifs" \
  -H "Authorization: Bearer $API_KEY"

Response (200)

{
  "data": [
    {
      "key": "catalog-profile",
      "label": "Katalogbild (Seitenansicht)",
      "look": "catalog",
      "shot": "profile",
      "surface": "none",
      "sizes": [
        {
          "key": "web",
          "width": 1600,
          "height": 256,
          "price": 1,
          "priceOnSurface": null
        },
        {
          "key": "print",
          "width": 3200,
          "height": 512,
          "price": 2,
          "priceOnSurface": null
        },
        {
          "key": "max",
          "width": 6400,
          "height": 1024,
          "price": 4,
          "priceOnSurface": null
        }
      ],
      "video": {
        "motions": [
          {
            "name": "turn",
            "seconds": 8,
            "loops": true,
            "surface": false,
            "lettered": false,
            "requires": []
          }
        ],
        "resolutions": [
          {
            "key": "hd",
            "width": 1280,
            "height": 204,
            "pricePerSecond": 1
          },
          {
            "key": "fullhd",
            "width": 1920,
            "height": 308,
            "pricePerSecond": 2
          }
        ]
      }
    }
  ],
  "options": {
    "formats": [
      "png",
      "jpg",
      "webp"
    ],
    "surfaces": [
      {
        "name": "concrete",
        "label": "Beton"
      }
    ],
    "poses": [
      "straight",
      "arc",
      "s-curve",
      "loop",
      "end-lift",
      "bend-90",
      "u-turn",
      "wave",
      "coil"
    ],
    "cuts": [
      "cores-out",
      "cores-stripped",
      "flush",
      "pie",
      "pie-stepped",
      "stepped",
      "straight"
    ],
    "videoFormats": [
      "mp4",
      "webm",
      "mov",
      "zip"
    ],
    "videoLengths": [
      "short",
      "standard",
      "long"
    ],
    "frameRates": [
      25,
      30,
      60
    ],
    "videoSurfacePricePercent": 150,
    "views": [
      {
        "key": "profile",
        "label": "Seitenansicht",
        "group": "whole",
        "environments": [
          "cutout",
          "white",
          "grey",
          "custom"
        ],
        "videoEnvironments": [
          "cutout",
          "white",
          "grey",
          "custom"
        ]
      }
    ],
    "environments": [
      {
        "key": "cutout",
        "label": "Freigestellt",
        "group": "plain",
        "looks": [
          "catalog"
        ],
        "colour": null,
        "surface": null,
        "pricePercent": 100
      },
      {
        "key": "white",
        "label": "Weiß",
        "group": "plain",
        "looks": [
          "catalog"
        ],
        "colour": "#ffffff",
        "surface": null,
        "pricePercent": 100
      }
    ],
    "compositions": [
      {
        "key": "stairs",
        "label": "Treppe",
        "fewest": 2,
        "most": 6,
        "film": false,
        "views": [
          {
            "key": "above",
            "label": "Von oben",
            "format": "4:5",
            "sizes": [
              {
                "key": "web",
                "width": 600,
                "height": 750
              },
              {
                "key": "print",
                "width": 1200,
                "height": 1500
              },
              {
                "key": "max",
                "width": 2400,
                "height": 3000
              }
            ]
          }
        ],
        "environments": [
          "cutout",
          "white",
          "grey",
          "custom",
          "studio-light",
          "studio-dark"
        ],
        "priceFactors": [
          {
            "count": 2,
            "percent": 150
          },
          {
            "count": 3,
            "percent": 200
          },
          {
            "count": 4,
            "percent": 250
          },
          {
            "count": 5,
            "percent": 300
          },
          {
            "count": 6,
            "percent": 350
          }
        ]
      }
    ]
  }
}

Get the price of an order

POST /api/v1/orders/pricePermission: Order

Returns the price of a selection in euros (amount, net) and whether the order would cost anything right now: nothing if the picture already exists or is being made. Nothing is ordered.

Request

curl -X POST "https://testing.cablecreations.de/api/v1/orders/price" \
  -H "Authorization: Bearer $API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"cable":"nym-j-3x1-5","view":"profile","environment":"white","format":"png","extraFormats":["jpg"],"size":"web"}'

Response (200)

{
  "state": "new",
  "amount": {
    "currency": "EUR",
    "netCents": 200
  }
}

Order a picture

POST /api/v1/ordersPermission: Order

Orders a picture of a cable, published or not. The Idempotency-Key header is required: repeat a call with the same key and nothing is ordered twice. 201 means newly ordered; it is charged once the picture is delivered. 200 means the picture already exists or is being made, at no cost.

Request

curl -X POST "https://testing.cablecreations.de/api/v1/orders" \
  -H "Authorization: Bearer $API_KEY" \
  -H "Idempotency-Key: 8f14e45f-ceea-467f-a9f1-0b6d2c3e4f50" \
  -H "Content-Type: application/json" \
  -d '{"cable":"nym-j-3x1-5","view":"profile","environment":"white","format":"png","extraFormats":["jpg"],"size":"web","expectedNetCents":200}'

Response (201)

{
  "id": "6b1f0c9e-7a2d-4e53-9c10-4d5e6f708192",
  "status": "queued",
  "source": "api",
  "cable": {
    "slug": "nym-j-3x1-5",
    "name": "NYM-J 3x1,5"
  },
  "variant": {
    "id": "3d0c7e52-6c1a-4a0e-9a57-0b6a4a3b7d10",
    "name": "3x1,5"
  },
  "motif": "catalog-profile",
  "kind": "image",
  "format": "png",
  "size": "web",
  "video": null,
  "scene": {
    "kind": "colour",
    "colour": "#ffffff"
  },
  "view": "profile",
  "environment": "white",
  "extraFormats": [
    "jpg"
  ],
  "pose": null,
  "cut": null,
  "cables": null,
  "composition": null,
  "compositionView": null,
  "amount": {
    "currency": "EUR",
    "netCents": 200,
    "state": "authorised"
  },
  "files": [],
  "createdAt": "2026-04-01T10:00:00.000Z",
  "finishedAt": null
}

List orders

GET /api/v1/ordersPermission: Order

The picture orders of your organization, newest first, including those placed in the portal (source).

Request

curl "https://testing.cablecreations.de/api/v1/orders" \
  -H "Authorization: Bearer $API_KEY"

Response (200)

{
  "data": [
    {
      "id": "6b1f0c9e-7a2d-4e53-9c10-4d5e6f708192",
      "status": "delivered",
      "source": "api",
      "cable": {
        "slug": "nym-j-3x1-5",
        "name": "NYM-J 3x1,5"
      },
      "variant": {
        "id": "3d0c7e52-6c1a-4a0e-9a57-0b6a4a3b7d10",
        "name": "3x1,5"
      },
      "motif": "catalog-profile",
      "kind": "image",
      "format": "png",
      "size": "web",
      "video": null,
      "scene": {
        "kind": "colour",
        "colour": "#ffffff"
      },
      "view": "profile",
      "environment": "white",
      "extraFormats": [
        "jpg"
      ],
      "pose": null,
      "cut": null,
      "cables": null,
      "composition": null,
      "compositionView": null,
      "amount": {
        "currency": "EUR",
        "netCents": 200,
        "state": "captured"
      },
      "files": [
        {
          "id": "b6f0a4d2-93c4-4a6b-a1b5-2f1f6f3f9c22",
          "name": "nym-j-3x1-5-catalog-profile.png",
          "url": "https://kabel.example/api/v1/orders/6b1f0c9e-7a2d-4e53-9c10-4d5e6f708192/files/b6f0a4d2-93c4-4a6b-a1b5-2f1f6f3f9c22",
          "mime": "image/png",
          "bytes": 184320,
          "width": 1600,
          "height": 256,
          "contentHash": "9f2c7d0b6a1e4c3f8b5d2a7e0c9f4b1d6e3a8c5f2b7d0e9a4c1f6b3d8e5a2c7f"
        },
        {
          "id": "c7a1b5e3-04d5-4b7c-b2c6-3a2a7a4a0d33",
          "name": "nym-j-3x1-5-catalog-profile.jpg",
          "url": "https://kabel.example/api/v1/orders/6b1f0c9e-7a2d-4e53-9c10-4d5e6f708192/files/c7a1b5e3-04d5-4b7c-b2c6-3a2a7a4a0d33",
          "mime": "image/jpeg",
          "bytes": 61440,
          "width": 1600,
          "height": 256,
          "contentHash": "4e1b8d3a6c0f2e5b9a7d1c4f8e2b6a0d3c9f5e1b7a4d0c6e2f8b5a1d9c3e7f0b"
        }
      ],
      "createdAt": "2026-04-01T10:00:00.000Z",
      "finishedAt": "2026-04-01T10:04:12.000Z"
    }
  ],
  "next": null
}

Retrieve an order

GET /api/v1/orders/{id}Permission: Order

The status of an order: queued, rendering, delivered, failed, or cancelled. Once delivered, the files are in files.

Request

curl "https://testing.cablecreations.de/api/v1/orders/6b1f0c9e-7a2d-4e53-9c10-4d5e6f708192" \
  -H "Authorization: Bearer $API_KEY"

Response (200)

{
  "id": "6b1f0c9e-7a2d-4e53-9c10-4d5e6f708192",
  "status": "delivered",
  "source": "api",
  "cable": {
    "slug": "nym-j-3x1-5",
    "name": "NYM-J 3x1,5"
  },
  "variant": {
    "id": "3d0c7e52-6c1a-4a0e-9a57-0b6a4a3b7d10",
    "name": "3x1,5"
  },
  "motif": "catalog-profile",
  "kind": "image",
  "format": "png",
  "size": "web",
  "video": null,
  "scene": {
    "kind": "colour",
    "colour": "#ffffff"
  },
  "view": "profile",
  "environment": "white",
  "extraFormats": [
    "jpg"
  ],
  "pose": null,
  "cut": null,
  "cables": null,
  "composition": null,
  "compositionView": null,
  "amount": {
    "currency": "EUR",
    "netCents": 200,
    "state": "captured"
  },
  "files": [
    {
      "id": "b6f0a4d2-93c4-4a6b-a1b5-2f1f6f3f9c22",
      "name": "nym-j-3x1-5-catalog-profile.png",
      "url": "https://kabel.example/api/v1/orders/6b1f0c9e-7a2d-4e53-9c10-4d5e6f708192/files/b6f0a4d2-93c4-4a6b-a1b5-2f1f6f3f9c22",
      "mime": "image/png",
      "bytes": 184320,
      "width": 1600,
      "height": 256,
      "contentHash": "9f2c7d0b6a1e4c3f8b5d2a7e0c9f4b1d6e3a8c5f2b7d0e9a4c1f6b3d8e5a2c7f"
    },
    {
      "id": "c7a1b5e3-04d5-4b7c-b2c6-3a2a7a4a0d33",
      "name": "nym-j-3x1-5-catalog-profile.jpg",
      "url": "https://kabel.example/api/v1/orders/6b1f0c9e-7a2d-4e53-9c10-4d5e6f708192/files/c7a1b5e3-04d5-4b7c-b2c6-3a2a7a4a0d33",
      "mime": "image/jpeg",
      "bytes": 61440,
      "width": 1600,
      "height": 256,
      "contentHash": "4e1b8d3a6c0f2e5b9a7d1c4f8e2b6a0d3c9f5e1b7a4d0c6e2f8b5a1d9c3e7f0b"
    }
  ],
  "createdAt": "2026-04-01T10:00:00.000Z",
  "finishedAt": "2026-04-01T10:04:12.000Z"
}

Cancel an order

POST /api/v1/orders/{id}/cancelPermission: Order

Cancels an order that is still waiting in the queue; you aren't charged for it. Once a picture is in progress, the call responds with 409.

Request

curl -X POST "https://testing.cablecreations.de/api/v1/orders/6b1f0c9e-7a2d-4e53-9c10-4d5e6f708192/cancel" \
  -H "Authorization: Bearer $API_KEY"

Response (200)

{
  "id": "6b1f0c9e-7a2d-4e53-9c10-4d5e6f708192",
  "status": "cancelled",
  "source": "api",
  "cable": {
    "slug": "nym-j-3x1-5",
    "name": "NYM-J 3x1,5"
  },
  "variant": {
    "id": "3d0c7e52-6c1a-4a0e-9a57-0b6a4a3b7d10",
    "name": "3x1,5"
  },
  "motif": "catalog-profile",
  "kind": "image",
  "format": "png",
  "size": "web",
  "video": null,
  "scene": {
    "kind": "colour",
    "colour": "#ffffff"
  },
  "view": "profile",
  "environment": "white",
  "extraFormats": [
    "jpg"
  ],
  "pose": null,
  "cut": null,
  "cables": null,
  "composition": null,
  "compositionView": null,
  "amount": {
    "currency": "EUR",
    "netCents": 200,
    "state": "authorised"
  },
  "files": [],
  "createdAt": "2026-04-01T10:00:00.000Z",
  "finishedAt": "2026-04-01T10:01:00.000Z"
}

Price of a conversion

POST /api/v1/conversions/pricePermission: Convert

Gives the price in euros (net) of a conversion with the outputs you choose. Needs no file and starts nothing.

Request

curl -X POST "https://testing.cablecreations.de/api/v1/conversions/price" \
  -H "Authorization: Bearer $API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"outputs":["dxf"]}'

Response (200)

{
  "currency": "EUR",
  "lines": [
    {
      "kind": "conversion",
      "name": "conversion",
      "cents": 3900
    },
    {
      "kind": "extra",
      "name": "dxf",
      "cents": 500
    }
  ],
  "netCents": 4400,
  "tier": {
    "fromJobs": 0,
    "percentOff": 0,
    "requestsPerWindow": 100
  }
}

Convert a spec

POST /api/v1/conversionsPermission: Convert

Checks a cable's spec (JSON) with the generator, reserves the price within your monthly limit, and builds the files with no further step, normally within a few minutes. A spec the generator refuses is answered at once with 400 and one error at its place each (field is the JSON path); nothing is reserved. Otherwise the call responds with 202. The Idempotency-Key header is required. Without an invoice address or with the monthly limit reached the call responds with 402, with too many conversions in progress with 429.

Request

curl -X POST "https://testing.cablecreations.de/api/v1/conversions" \
  -H "Authorization: Bearer $API_KEY" \
  -H "Idempotency-Key: 2c6f8a1e-9d3b-4e75-a0c4-7b1d5f3e8a92" \
  -H "Content-Type: application/json" \
  -d '{"spec":{"name":"RG 58 C/U","cut":{"preset":"stepped","at":68},"build":[{"conductor":{"diameter":0.9,"strand_pitch":0.19,"material":"copper-tinned","color":"#c9cdd1"}},{"insulation":{"thickness":1.02,"material":"rubber","color":"#d3dde3"}},{"braid":{"carriers":12,"wires_per_carrier":8,"wire_diameter":0.12,"lay_length":20,"material":"copper-tinned","color":"#c9cdd1"}},{"sheath":{"thickness":0.76,"material":"pvc","color":"#1e1e20"}}]},"outputs":["dxf"]}'

Response (202)

{
  "id": "cv_4f7a1c9e2b8d4a6f9c3e5d7b1a2c4e6f",
  "status": "queued",
  "source": "spec_json",
  "size": "RG 58 C/U",
  "outputs": [
    "glb_web",
    "section_svg",
    "check_json",
    "dxf"
  ],
  "amount": {
    "currency": "EUR",
    "netCents": 4400,
    "state": "authorised"
  },
  "refusal": null,
  "files": [],
  "cable": null,
  "filesKeptUntil": null,
  "createdAt": "2026-04-01T10:02:00.000Z",
  "finishedAt": null
}

List conversions

GET /api/v1/conversionsPermission: Convert

All conversions of your organization, newest first, with their status and files.

Request

curl "https://testing.cablecreations.de/api/v1/conversions" \
  -H "Authorization: Bearer $API_KEY"

Response (200)

{
  "data": [
    {
      "id": "cv_4f7a1c9e2b8d4a6f9c3e5d7b1a2c4e6f",
      "status": "completed",
      "source": "spec_json",
      "size": "RG 58 C/U",
      "outputs": [
        "glb_web",
        "section_svg",
        "check_json",
        "dxf"
      ],
      "amount": {
        "currency": "EUR",
        "netCents": 4400,
        "state": "captured"
      },
      "refusal": null,
      "files": [
        {
          "id": "e1c7b0a4-5d2f-4a39-8c6e-9b3d1f7a2c58",
          "name": "rg-58-c-u-3d-web.glb",
          "kind": "glb_web",
          "mime": "model/gltf-binary",
          "bytes": 412860,
          "url": "https://kabel.example/api/v1/conversions/cv_4f7a1c9e2b8d4a6f9c3e5d7b1a2c4e6f/files/e1c7b0a4-5d2f-4a39-8c6e-9b3d1f7a2c58",
          "contentHash": "3b5d9e1f7a2c4806b1d3f5a7c9e0b2d4f6a8c1e3b5d7f9a0c2e4b6d8f1a3c5e7"
        }
      ],
      "cable": {
        "slug": "rg-58-c-u",
        "name": "RG 58 C/U"
      },
      "filesKeptUntil": "2027-03-31T22:00:00.000Z",
      "createdAt": "2026-04-01T10:02:00.000Z",
      "finishedAt": "2026-04-01T10:04:00.000Z"
    }
  ],
  "next": null
}

Retrieve a conversion

GET /api/v1/conversions/{id}Permission: Convert

The conversion’s status: queued, reading, in_review, building, completed, refused (with a code and a sentence) or cancelled. What is delivered is a 3D model for the web (GLB), the cross-section drawing (SVG) and the check report (JSON), more formats on request.

Request

curl "https://testing.cablecreations.de/api/v1/conversions/cv_4f7a1c9e2b8d4a6f9c3e5d7b1a2c4e6f" \
  -H "Authorization: Bearer $API_KEY"

Response (200)

{
  "id": "cv_4f7a1c9e2b8d4a6f9c3e5d7b1a2c4e6f",
  "status": "completed",
  "source": "spec_json",
  "size": "RG 58 C/U",
  "outputs": [
    "glb_web",
    "section_svg",
    "check_json",
    "dxf"
  ],
  "amount": {
    "currency": "EUR",
    "netCents": 4400,
    "state": "captured"
  },
  "refusal": null,
  "files": [
    {
      "id": "e1c7b0a4-5d2f-4a39-8c6e-9b3d1f7a2c58",
      "name": "rg-58-c-u-3d-web.glb",
      "kind": "glb_web",
      "mime": "model/gltf-binary",
      "bytes": 412860,
      "url": "https://kabel.example/api/v1/conversions/cv_4f7a1c9e2b8d4a6f9c3e5d7b1a2c4e6f/files/e1c7b0a4-5d2f-4a39-8c6e-9b3d1f7a2c58",
      "contentHash": "3b5d9e1f7a2c4806b1d3f5a7c9e0b2d4f6a8c1e3b5d7f9a0c2e4b6d8f1a3c5e7"
    }
  ],
  "cable": {
    "slug": "rg-58-c-u",
    "name": "RG 58 C/U"
  },
  "filesKeptUntil": "2027-03-31T22:00:00.000Z",
  "createdAt": "2026-04-01T10:02:00.000Z",
  "finishedAt": "2026-04-01T10:04:00.000Z"
}

Cancel a conversion

POST /api/v1/conversions/{id}/cancelPermission: Convert

Cancels a conversion before it is built; the hold on your card is released. After that the call responds with 409.

Request

curl -X POST "https://testing.cablecreations.de/api/v1/conversions/cv_4f7a1c9e2b8d4a6f9c3e5d7b1a2c4e6f/cancel" \
  -H "Authorization: Bearer $API_KEY"

Response (200)

{
  "id": "cv_4f7a1c9e2b8d4a6f9c3e5d7b1a2c4e6f",
  "status": "cancelled",
  "source": "spec_json",
  "size": "RG 58 C/U",
  "outputs": [
    "glb_web",
    "section_svg",
    "check_json",
    "dxf"
  ],
  "amount": {
    "currency": "EUR",
    "netCents": 4400,
    "state": "released"
  },
  "refusal": null,
  "files": [],
  "cable": null,
  "filesKeptUntil": null,
  "createdAt": "2026-04-01T10:02:00.000Z",
  "finishedAt": "2026-04-01T10:05:00.000Z"
}

Retrieve a cable's datasheet

GET /api/v1/cables/{slug}/datasheetPermission: Read

The datasheet of a published cable as a PDF in your branding: type data, public images, and the size table. Each cable carries the address in its datasheetUrl field.

Response (200) · application/pdf

Download a delivered file

GET /api/v1/orders/{id}/files/{fileId}Permission: Order

The file itself. The call needs the API key like any other; each file carries the address in its url field.

Response (200) · application/octet-stream

A conversion’s file

GET /api/v1/conversions/{id}/files/{fileId}Permission: Convert

The file itself. The call needs the API key like every other; each file carries its address in the url field.

Response (200) · application/octet-stream

Ordering pictures

With the Order permission, your systems place the same picture orders your team places in the portal, at the same prices and under the same rules.

  • The flow: retrieve motifs and prices, optionally get the price of a selection, order, check the status (or get notified by webhook), download the files.
  • Name a picture by view and setting: view (where the camera looks from) and environment (where the cable lies – background and ground always match), for custom with environmentColour. options.views and options.environments of the motif list say which pairs can be ordered. The older form with motif and scene still works; both in one order is rejected.
  • One order is one picture size (web, print or max) or one video resolution; order several with several calls. Further file formats of the same picture or video go into extraFormats (for example png with jpg beside it); they cost nothing. A cut-out’s JPG or MP4 is placed on white.
  • Videos come in HD, Full HD and 4K (resolution uhd, 3,840 pixels on the longer side). 4K comes in every length and frame rate. A file that would exceed 1 GiB (such as ProRes or PNG frames of a long 4K video) is rejected with too_large; MP4 and WebM always fit.
  • Every order needs the Idempotency-Key header with a value of your choice (a UUID, for example). The same value with the same body never orders twice; the response then carries Idempotent-Replay: true. The same value with a different body is rejected with 409.
  • If the picture already exists or is being made, the order costs nothing (200 instead of 201).
  • Use expectedNetCents as a safeguard, with the net price in cents that the price query gave (amount.netCents): if a new picture costs something else, nothing is ordered (409 price_changed).
  • If the order exceeds the spending limit, the key’s budget is used up, or ordering is paused, the API responds with 402 and a code of its own. Nothing is ordered; the same Idempotency-Key stays usable afterward.
  • Prices: amount gives the price net, in cents (netCents), with its currency. Manufacturers pay with the monthly invoice, developers with their monthly collective invoice within their monthly limit; a failed or canceled picture isn’t charged.
  • Orders are attributed to the person who created the key and appear in the portal under Orders.
  • Download delivered files with the key from the address in files[].url. The addresses do not work without the key.
  • Each key can place 60 orders in 10 minutes; beyond that, the API responds with 429.

Several cables in one picture

  • Instead of cable, name cables: two to six cables in the order of the picture (left to right, front to back), each with cable and optionally variant. The same cable may appear more than once.
  • Add composition (how the cables lie together) and compositionView (the angle; without it, the first one offered) and environment – cut out, on a color or in a studio, not on a ground. options.compositions of the motif list says which arrangements can be ordered, with which angles, sizes and settings.
  • A cut applies to all cables and is possible only when every cable allows it. pose, view, motif and scene do not exist for several cables; a video only twisted, from the side (compositionView side; “With cable ends” is a picture only).
  • Every further cable adds half of the single price (priceFactors). The price query gives the price of the selection; expectedNetCents, Idempotency-Key and the responses 200, 402 and 409 work as for one cable.
  • Rejections name each field: cables[i] with unknown_cable or not_ready, composition with not_offered, count_needs or a reason of the bundle (helix_sizes_apart, helix_too_stiff, helix_too_short), environment with not_with_several, video with only_helix, compositionView with unavailable (a video of the cable ends).
  • The order names cables (with position), composition and compositionView; cable and variant name the first cable. Webhooks carry the same order.

Request

curl -X POST "https://testing.cablecreations.de/api/v1/orders" \
  -H "Authorization: Bearer $API_KEY" \
  -H "Idempotency-Key: 4b7e2d90-6c1f-4a83-b5d2-9e0f1a3c5e77" \
  -H "Content-Type: application/json" \
  -d '{"cables":[{"cable":"nym-j-3x1-5"},{"cable":"nym-j-3x2-5"},{"cable":"nym-j-5x1-5"}],"composition":"stairs","compositionView":"above","environment":"cutout","format":"png","size":"web","expectedNetCents":1200}'

Response (201)

{
  "id": "8c2d4e6f-1a3b-4c5d-9e7f-2b4d6f8a0c13",
  "status": "queued",
  "source": "api",
  "cable": {
    "slug": "nym-j-3x1-5",
    "name": "NYM-J 3x1,5"
  },
  "variant": {
    "id": "3d0c7e52-6c1a-4a0e-9a57-0b6a4a3b7d10",
    "name": "3x1,5"
  },
  "motif": "photo-stairs",
  "kind": "image",
  "format": "png",
  "size": "web",
  "video": null,
  "scene": {
    "kind": "transparent"
  },
  "view": null,
  "environment": "cutout",
  "extraFormats": [],
  "pose": null,
  "cut": null,
  "cables": [
    {
      "position": 0,
      "cable": {
        "slug": "nym-j-3x1-5",
        "name": "NYM-J 3x1,5"
      },
      "variant": {
        "id": "3d0c7e52-6c1a-4a0e-9a57-0b6a4a3b7d10",
        "name": "3x1,5"
      }
    },
    {
      "position": 1,
      "cable": {
        "slug": "nym-j-3x2-5",
        "name": "NYM-J 3x2,5"
      },
      "variant": {
        "id": "5a7c9e1b-3d5f-4a7c-8e0b-2d4f6a8c0e21",
        "name": "3x2,5"
      }
    },
    {
      "position": 2,
      "cable": {
        "slug": "nym-j-5x1-5",
        "name": "NYM-J 5x1,5"
      },
      "variant": {
        "id": "7e9a1c3d-5f7b-4c9e-a1d3-5f7b9d1e3a42",
        "name": "5x1,5"
      }
    }
  ],
  "composition": "stairs",
  "compositionView": "above",
  "amount": {
    "currency": "EUR",
    "netCents": 1200,
    "state": "authorised"
  },
  "files": [],
  "createdAt": "2026-04-01T10:00:00.000Z",
  "finishedAt": null
}

Converting specs

With developer access and the Convert permission, your systems turn a cable's spec into a 3D cable: a 3D model for the web (GLB), the cross-section drawing (SVG) and a check report (JSON), more formats on request. You pay only for what is delivered.

Nobody on our side works on it: the generator checks the spec when you call and builds it right after. That normally takes a few minutes.

  • The flow: get the price, start the conversion with the spec and the outputs, check the status (or get notified by the webhooks conversion.completed and conversion.refused), download the files.
  • A conversion is exactly one cable in one size. size names the size (for example 3G1,5); without size, the spec's name is used.
  • Every conversion needs the Idempotency-Key header. The same value with the same body never starts twice (the response then carries Idempotent-Replay: true); with a different body it is rejected with 409.
  • At the start the price is reserved within your monthly limit; it is charged on delivery, on your monthly invoice. If a conversion is refused or cancelled, we release the reservation. Without an invoice address or with the monthly limit reached the call responds with 402 and a reason, with too many conversions in progress with 429.
  • You can cancel while the conversion waits (queued); after that the call responds with 409.
  • The data in the spec must be yours or you must be allowed to use it. You may use the delivered files in your own product.
  • Retention: the delivered files 12 months after delivery (filesKeptUntil names the day). The cable with its spec stays in your account.
  • Pictures of your cable: a delivered conversion names its cable (cable.slug). With the Order permission you order pictures of it through POST /orders. Each new picture is reserved within your monthly limit (402 and 429 as above) and charged when it is delivered; amount in the answer names the price in euros. A picture that exists or is under way costs nothing again.

The spec as JSON

  • spec is the generator's spec format as JSON: a table is an object, an array of tables an array, a build step an object with one key ({"conductor": {…}}). Lengths in millimeters.
  • Allowed are strings, finite numbers, true and false, arrays and objects; keys of letters, digits, _ and -. null, infinite numbers and arrays of arrays are refused before the generator runs (not_an_object, invalid_key, invalid_value, too_large from 256 KB).
  • The generator checks the spec like cable check: each error comes as its own detail with the JSON path (spec.build[2].conductor.diameter), its code and its sentence. An outer diameter off the target under [datasheet] is an error too.
  • A test key's spec is checked the same way; only the delivery is a sample.

Prices (net, in euros)

  • One size with the 3D model for the web, the cross-section drawing and the check report: €39.00
  • Additionally Detail model with all wires (GLB, outputs: glb_full): €5.00
  • Additionally OBJ with materials (ZIP, outputs: obj): €5.00
  • Additionally STL (outputs: stl): €5.00
  • Additionally FBX for Blender, Unity and Unreal (outputs: fbx): €9.00
  • Additionally USDZ for Apple (outputs: usdz): €9.00
  • Additionally Cross-section drawing (DXF, outputs: dxf): €5.00

States of a conversion

  • queued The spec passed the check; the conversion waits to be built.
  • reading Only on conversions of the former datasheet path: the datasheet was read.
  • in_review Only on conversions of the former datasheet path: the construction was checked.
  • building The files are being built.
  • completed Delivered and charged; files holds the files.
  • refused Not delivered; refusal names a code and a sentence, nothing is charged.
  • cancelled Cancelled by you; the reservation is released.

Reasons for a refusal (refusal.code)

  • unreadable_datasheet The datasheet cannot be read (a scan, damaged, no usable text).
  • not_a_cable The document does not describe a cable we can build.
  • size_not_found The datasheet has no size with this designation.
  • not_buildable The spec could not be built.
  • diameter_off_target The construction we built differs from the datasheet in outer diameter.
  • not_delivered_in_time The conversion was not finished in time; the reservation was released.
  • other Another reason; refusal.message names it.

Webhooks

Instead of polling orders and cables, get notified. Owners set up the endpoints under Settings → Webhooks.

  • Every event arrives as JSON in a POST request with the same envelope: id, type, version, createdAt, and data. An event’s id stays the same on every retry; use it to detect duplicate deliveries.
  • data contains only IDs and what the API already shows your key. For render.finished it is the order with its files; for enquiry.received it is only the inquiry’s ID and the cable, with nothing about the person who asked. The only exception are the lead.* events, see below.
  • Respond with a 2xx status within ten seconds. Anything else, including a redirect, counts as failed and is retried: after 1, 4, 16, and 64 minutes and after a little over 4 hours, six attempts in total.
  • If deliveries fail for three days, we disable the endpoint and notify the owners by email.
  • The address must start with https:// and be reachable on the public internet.

Events

  • render.finished An ordered picture was delivered.
  • render.failed An ordered picture failed for good.
  • cable.published A cable was published.
  • cable.updated The data of a published cable was changed.
  • cable.unpublished A cable was removed from the catalog.
  • request.status_changed A cable order has a new state: every step from received to accepted.
  • service_order.status_changed An order for a design service has a new state: every step from received to accepted.
  • enquiry.received An inquiry came in through the catalog.
  • lead.catalogue_enquiry An inquiry from the catalog, with the name, email, company, and message of the person who asked.
  • lead.expose_reply A reply to a showcase, with the name, email, and message of the person who replied and the cables they checked.
  • conversion.completed A conversion was delivered; its files are ready.
  • conversion.refused A conversion was refused; nothing was charged.

Example delivery

{
  "id": "evt_3f1d6c0a-9b2e-4c57-8d10-7a6b5c4d3e2f",
  "type": "render.finished",
  "version": 1,
  "createdAt": "2026-04-01T10:04:13.000Z",
  "data": {
    "order": {
      "id": "6b1f0c9e-7a2d-4e53-9c10-4d5e6f708192",
      "status": "delivered",
      "source": "api",
      "cable": {
        "slug": "nym-j-3x1-5",
        "name": "NYM-J 3x1,5"
      },
      "variant": {
        "id": "3d0c7e52-6c1a-4a0e-9a57-0b6a4a3b7d10",
        "name": "3x1,5"
      },
      "motif": "catalog-profile",
      "kind": "image",
      "format": "png",
      "size": "web",
      "video": null,
      "scene": {
        "kind": "colour",
        "colour": "#ffffff"
      },
      "view": "profile",
      "environment": "white",
      "extraFormats": [],
      "pose": null,
      "cut": null,
      "cables": null,
      "composition": null,
      "compositionView": null,
      "amount": {
        "currency": "EUR",
        "netCents": 200,
        "state": "captured"
      },
      "files": [
        {
          "id": "b6f0a4d2-93c4-4a6b-a1b5-2f1f6f3f9c22",
          "name": "nym-j-3x1-5-catalog-profile.png",
          "url": "https://kabel.example/api/v1/orders/6b1f0c9e-7a2d-4e53-9c10-4d5e6f708192/files/b6f0a4d2-93c4-4a6b-a1b5-2f1f6f3f9c22",
          "mime": "image/png",
          "bytes": 184320,
          "width": 1600,
          "height": 256,
          "contentHash": "9f2c7d0b6a1e4c3f8b5d2a7e0c9f4b1d6e3a8c5f2b7d0e9a4c1f6b3d8e5a2c7f"
        }
      ],
      "createdAt": "2026-04-01T10:00:00.000Z",
      "finishedAt": "2026-04-01T10:04:12.000Z"
    }
  }
}

Cable orders: request.status_changed

The event is sent for every step of a cable order. status and previousStatus are the request’s status with its four values, as GET /requests shows it. orderState and previousOrderState say more precisely where the order stands, and actsNext who it is waiting for: customer, operator, or nobody. When only the order moves (a question, a round of changes, or the acceptance), status and previousStatus are equal. A quote and a plain message change no state and send nothing.

Values of orderState

  • submitted The order has been received.
  • in_progress We are working on the cable.
  • question_to_customer We have a question. The order is waiting for your answer.
  • delivered A version has been delivered and is waiting for your acceptance.
  • change_requested You requested changes. We are working on the next version.
  • accepted The version is accepted, by you or because the acceptance period ended. Only now can the cable be published.
  • rejected We rejected the order.
  • withdrawn You withdrew the order.

Example: a version was accepted

{
  "id": "evt_8c2a41d7-5e0b-4f36-a9c1-2b3d4e5f6a70",
  "type": "request.status_changed",
  "version": 1,
  "createdAt": "2026-04-08T09:15:02.000Z",
  "data": {
    "request": {
      "id": "0c9d2f5a-1b3e-4c7d-8e9f-a1b2c3d4e5f6",
      "status": "delivered",
      "previousStatus": "delivered",
      "orderState": "accepted",
      "previousOrderState": "delivered",
      "actsNext": "nobody"
    }
  }
}

Verify the signature

Every delivery carries the header Cable-Signature: t=<timestamp>,v1=<signature>. The signature is the HMAC-SHA256 of “<timestamp>.<unmodified body>” with your signing secret. Verify it over the raw body, compare in constant time, and reject timestamps older than five minutes.

import { createHmac, timingSafeEqual } from "node:crypto";

// header: the value of the Cable-Signature header
// rawBody: the request body exactly as it arrived (text, not parsed JSON)
export function isFromCablePlatform(rawBody, header, secret) {
  const match = /^t=(\d+),v1=([0-9a-f]{64})$/.exec(header ?? "");
  if (match === null) return false;
  const [, timestamp, signature] = match;
  const ageSeconds = Math.abs(Date.now() / 1000 - Number(timestamp));
  if (ageSeconds > 300) return false;
  const expected = createHmac("sha256", secret)
    .update(`${timestamp}.${rawBody}`)
    .digest("hex");
  return timingSafeEqual(Buffer.from(signature, "hex"), Buffer.from(expected, "hex"));
}

Pagination

  • Lists return data and next. If next is not null, get the next page with ?cursor=<next>.
  • Use limit to set the page size: 1 to 100; the default is 50.
  • The cursor is opaque: do not build or modify it.
  • If new entries are added while you are paging, no entry is returned twice and none is skipped.

Errors

Errors always have the same shape. Evaluate code, not the text in message.

StatusCodeMeaning
401unauthorizedThe key is missing, unknown, or has been revoked.
403forbiddenThe key lacks the permission for this call.
404not_foundThere is nothing under this name that the key is allowed to see. Unpublished cables respond this way too.
400validation_failedA parameter or field was rejected. details lists every affected field with a code.
429rate_limitedToo many requests. Retry-After gives the wait time in seconds.
500internalAn error on our side. Try again later.
402spending_limit_reachedThe order exceeds your organization’s monthly spending limit.
402budget_exceededThe order exceeds the budget of the key, of the guest it belongs to, or your own monthly limit.
402billing_blockedOrdering is currently paused for your organization. Owners see the reason under Billing.
409price_changedThe order costs something other than the expectedNetCents you sent. Get the price again.
409idempotency_conflictThis Idempotency-Key was already used for a different order.
409not_cancellableThe order is no longer waiting in the queue.
403plan_requiredAPI access is not part of your organization’s plan. The key is kept and works again once the plan includes it. With the reason storage_full (when ordering): your organization’s storage is full; reading calls still work.
402payment_requiredNothing was started and nothing is charged. reason is invoice_address_required (your organization has no invoice address for its monthly invoice yet) or monthly_limit_reached (the job would exceed the owner’s monthly limit).
429too_many_jobsToo many conversions are in progress for your organization.
503service_unavailableThe payment provider cannot be reached; nothing was started.

Examples

Response (400)

{
  "error": {
    "code": "validation_failed",
    "message": "The request is not valid. See `details`.",
    "details": [
      {
        "field": "title",
        "code": "required",
        "message": "title is required."
      },
      {
        "field": "construction",
        "code": "no_number",
        "message": "construction must contain a number."
      }
    ]
  }
}

Response (401)

{
  "error": {
    "code": "unauthorized",
    "message": "Missing or invalid API key. Send it as `Authorization: Bearer <key>`."
  }
}

Response (403)

{
  "error": {
    "code": "forbidden",
    "message": "This API key does not have the scope this call needs."
  }
}

Response (404)

{
  "error": {
    "code": "not_found",
    "message": "Not found."
  }
}

Response (429)

{
  "error": {
    "code": "rate_limited",
    "message": "Too many requests. Wait for the time given in `Retry-After`."
  }
}

Response (402)

{
  "error": {
    "code": "spending_limit_reached",
    "message": "This order exceeds the organization's monthly spending limit."
  }
}

Response (409)

{
  "error": {
    "code": "idempotency_conflict",
    "message": "This `Idempotency-Key` was already used for another order. Use a new key for a new order."
  }
}

Response (402)

{
  "error": {
    "code": "payment_required",
    "message": "This job would exceed the monthly limit, so nothing was started. Its owner can raise the limit under Usage & invoices.",
    "reason": "monthly_limit_reached"
  }
}

Limits

  • Each key allows 100 requests in a row. After 10 seconds without a request, the full quota is available again.
  • When the quota is used up, the API responds with 429 and gives the wait time in seconds in Retry-After.
  • Do not fetch data on every page view of your website; cache it instead.