> ## Documentation Index
> Fetch the complete documentation index at: https://www.zippendo.com/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Egne kanaler

> Forbind din egen webshop eller dit ERP-system til Zippendo med signerede ordrer, der sendes ind, og REST API'et.

En egen kanal lader din egen webshop, dit ERP-system eller en anden ordrekilde, du selv kontrollerer, sende ordrer ind i Zippendo, når der ikke findes en færdig forbindelse til den. Den virker i to retninger:

* **Send ordrer ind**: dit system sender signerede webhook-hændelser til kanalens egen **ingest-URL** eller kalder [REST API'et](/docs/api-reference/overview) (officielle SDK'er findes).
* **Få svar tilbage**: abonner på Zippendos [udgående webhooks](/docs/da/quickstarts/webhooks) (`shipment.dispatched`, `tracking.updated` med flere) for at modtage trackingnumre og statusændringer.

## 1. Opret kanalen

Klik på **Ordrekanaler** i [app.zippendo.com](https://app.zippendo.com), åbn fanen **Katalog**, og klik på **Opret kanal** på kortet **Egen kanal**. Eller opret den gennem API'et:

```bash theme={null}
curl -X POST https://api.zippendo.com/orgs/{orgId}/order-channels \
  -H "Authorization: Bearer zipp_..." \
  -H "Content-Type: application/json" \
  -d '{ "name": "My webshop", "type": "custom" }'
```

## 2. Generer signeringsnøglen

Klik på **Generer nøgle** på kanalens indstillingsside, eller kald `POST /orgs/{orgId}/order-channels/{channelId}/webhook-secret`. Svaret indeholder **ingest-URL'en** og **hemmeligheden**:

```json theme={null}
{
  "secret": "zwhs_...",
  "webhookUrl": "https://api.zippendo.com/webhooks/order-channels/{channelId}",
  "createdAt": "2026-09-02T12:00:00.000Z"
}
```

<Warning>
  Hemmeligheden vises **kun én gang**. Gem den i dit systems konfiguration. Hvis du erstatter hemmeligheden, oprettes en ny, og den gamle holder op med at virke med det samme. Hvis du tilbagekalder den, afviser ingest-URL'en alle forsendelser af ordrer, indtil der findes en ny hemmelighed.
</Warning>

## 3. Send ordrer

Send en `POST` til ingest-URL'en med to headers:

| Header | Værdi |
| - | - |
| `X-Zippendo-Topic` | `order.created`, `order.updated`, `order.cancelled` eller `ping` |
| `X-Zippendo-Signature` | HMAC-SHA256 af den **rå request body**, hex-kodet, med din signeringshemmelighed som nøgle |

```javascript theme={null}
import crypto from "node:crypto"

const body = JSON.stringify(order)
const signature = crypto.createHmac("sha256", process.env.ZIPPENDO_WEBHOOK_SECRET)
  .update(body)
  .digest("hex")

await fetch(webhookUrl, {
  method: "POST",
  headers: {
    "Content-Type": "application/json",
    "X-Zippendo-Topic": "order.created",
    "X-Zippendo-Signature": signature
  },
  body
})
```

### Ordrepayload (`order.created` / `order.updated`)

Begge emner opfører sig ens: ordren **upserts** på `externalId`, så gentagne leveringer og opdateringer i forkert rækkefølge er sikre. Send det, der er nemmest for dit system.

```json theme={null}
{
  "externalId": "SO-10422",
  "orderNumber": "#1042",
  "customerName": "Anna Jensen",
  "customerEmail": "anna@example.dk",
  "shippingAddress": {
    "name": "Anna Jensen",
    "address1": "Nørregade 12",
    "city": "København",
    "postalCode": "1165",
    "countryCode": "DK",
    "phone": "+45 12 34 56 78"
  },
  "orderLines": [
    { "name": "Wool Sweater", "sku": "SKU-1042-BLK", "quantity": 2, "unitPrice": 499.0, "weight": 0.5, "weightUnit": "kg" }
  ],
  "totalAmount": 998.0,
  "currency": "DKK",
  "shippingMethodTitle": "GLS Hjemmelevering"
}
```

| Felt | Obligatorisk | Noter |
| - | - | - |
| `externalId` | ✔ | Dit systems ordre-id, bruges som idempotensnøgle |
| `orderNumber` | ✔ | Læsbart ordrenummer |
| `orderLines` | ✔ | Mindst én linje (`name`, `quantity`, eventuelt `sku`, `unitPrice`, `weight`, …) |
| `shippingAddress` | | Skal bruges, for at der kan oprettes en forsendelse |
| `shippingMethodTitle` | | Matches mod kanalens mappings af leveringsmetoder for at vælge en forsendelsesregel |
| `serviceCode` | | Eksplicit kode `{carrier}:{product}:{ruleId}[\|{servicePointId}]`, som går forud for mappingen |
| `customerName`, `customerEmail`, `subtotalAmount`, `totalAmount`, `currency`, `notes` | | Valgfrie metadata |

### Annulleringspayload (`order.cancelled`)

```json theme={null}
{ "externalId": "SO-10422" }
```

### Forbindelsestest (`ping`)

Enhver signeret body med `X-Zippendo-Topic: ping` returnerer `200 { "received": true }` uden at ændre data. Brug den til at tjekke din signaturkode.

### Svar

| Status | Betydning |
| - | - |
| `200` | Leveringen blev accepteret og behandlet |
| `400` | Manglende eller ukendt topic-header, en payload, der ikke bestod valideringen, eller en kanal, der er slået fra (**Kanal aktiveret** er slået fra) |
| `401` | Signaturverifikationen fejlede (forkert hemmelighed, manglende header eller ingen hemmelighed oprettet endnu) |
| `404` | Ukendt kanal-id |
| `429` | [Grænsen for ingest](/docs/api-reference/rate-limits) er nået. Prøv igen efter forsinkelsen i `Retry-After` |
| `5xx` | Behandlingen fejlede. Prøv leveringen igen: upsert gør gentagne leveringer sikre |

## Det sker med en sendt ordre

1. Ordren upserts i kanalen (og arver kanalens [brand](/docs/da/settings/brands), hvis der er et).
2. En forsendelsesregel findes: den eksplicitte `serviceCode`, ellers kanalens **mappings af leveringsmetoder** matchet mod `shippingMethodTitle`.
3. En forsendelse oprettes. Forsendelser med en matchet regel er klar til at blive sendt. Hvis kun en **Standard afsenderadresse** er sat, oprettes i stedet en kladde.
4. Hvis **Send forsendelser automatisk** er slået til for kanalen, sendes en forsendelse med en matchet regel straks til transportøren, og labelen lander i Zippendo (automatisk udskrivning på reglen gælder).

## REST som alternativ

Hvis du foretrækker request/response frem for webhooks, kan du oprette ordrer gennem API'et med et [API-token](/docs/da/settings/api-settings):

```bash theme={null}
curl -X POST https://api.zippendo.com/orgs/{orgId}/orders \
  -H "Authorization: Bearer zipp_..." \
  -H "Content-Type: application/json" \
  -d '{
    "orderChannelId": "{channelId}",
    "orderNumber": "#1042",
    "externalId": "SO-10422",
    "orderLines": [{ "name": "Wool Sweater", "quantity": 2 }],
    "shippingAddress": { "name": "Anna Jensen", "address1": "Nørregade 12", "city": "København", "postalCode": "1165", "countryCode": "DK" },
    "shippingMethodTitle": "GLS Hjemmelevering"
  }'
```

* Et duplikeret `externalId` på samme kanal returnerer **`409 ORDER_EXISTS`**. Behandl det som "allerede modtaget".
* Hvis du angiver `shippingRuleId` eller `shippingMethodTitle` (eller slår **Send forsendelser automatisk** til for kanalen), oprettes forsendelsen i samme kald, præcis som ved en sendt ordre.
* Ordrekanaler er en del af det offentlige API, så [SDK'erne](/docs/da/sdks/overview) dækker alle trin: opret kanalen, generer hemmeligheden, send ordrer og list forsendelser.

## Få tracking tilbage

Zippendo giver dit system besked gennem [udgående webhooks](/docs/da/quickstarts/webhooks): abonner på `shipment.dispatched` og `tracking.updated` under **Indstillinger → Webhooks**. Forsendelser har dit ordrenummer som `reference`, og payloaden indeholder `orderId` til opslag.

## Næste trin

<CardGroup cols={2}>
  <Card title="Udgående webhooks" icon="webhook" href="/docs/da/quickstarts/webhooks">
    Modtag tracking- og statushændelser, og verificer vores signaturer.
  </Card>

  <Card title="API-reference" icon="terminal" href="/docs/api-reference/overview">
    Alle endepunkter for ordrekanaler og ordrer med eksempler.
  </Card>
</CardGroup>


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.