## POST /api/v2/orders

**Create an order**

Create an order using public product slugs, public IDs, and customer-facing fields. Send a caller-generated `attemptKey` for retry-safe order creation: generate a fresh value per logical checkout/order attempt, then reuse the same value only when retrying that same request after a timeout or lost response. Repeats with the same key can return the existing order/invoice for the previous attempt within the duplicate-prevention window (currently one hour). Authenticated API callers normally omit `clientData`; the server uses the stored account profile. Use the `write:orders` scope for least-privilege API keys; broader `write:billing`, `write:services`, and `write:all` keys remain compatible. `transfer:domains` is for outgoing transfer/EPP-code actions and is not sufficient by itself to place a transfer-in order. Use `clientData` only to override or provide missing account/profile fields. Use item-level `registrantContact` when a domain should be registered to a different person or organisation than the account owner. For regular fixed-cycle VPS orders, first call `GET /api/v2/products/vps` and send one of its `data[].slug` values as `items[].productSlug`; do not use this endpoint for pay-as-you-go Cloud VPS creation. PAYG Cloud VPS deployment uses `POST /api/v2/vps/payg/deployments`. For shared hosting orders, first call `GET /api/v2/products/shared-hosting`, send one of its `data[].slug` values as `items[].productSlug`, and set `items[].primaryDomain` to the domain the hosting account should use. For domain registration, first call `GET /api/v2/products/domains/{tld}` and `GET /api/v2/domains/availability?name=example.se`; copy required item-level values such as `acceptedTerms`, `eppCode`, `premium`, `requiresRegistrarFeeAcceptance`, `nameservers`, or `copyExistingDns` into the order item. If the caller's stored profile is missing a registry-required field, either update `PATCH /api/v2/me` before ordering or include a partial `clientData` override. Payable orders return an invoice reference and may include a checkout URL. Payment preferences are checkout hints for the invoice created by the order. Use `card`, `swish`, `bankgiro`, `sepa`, or `invoice` when no specific payment flow should start. Account credit is applied after invoice creation through the invoice credit action.

### Related Endpoints

- `GET /api/v2/products/domains/{tld}`: Get TLD pricing and registry requirements
- `GET /api/v2/domains/availability`: Check domain availability
- `POST /api/v2/domains/availability`: Check bulk domain availability

### Headers

- `Accept`: application/json
- `Authorization`: Bearer YOUR_API_KEY
- Required API scope: `write:orders`
- `Content-Type`: application/json

### Request Body

- `items` (array<any>, optional)
- `clientData` (object, optional): Optional for authenticated API-key/session orders. Use only to override or provide missing profile fields returned by availability/validation requirements. Required for unauthenticated signup orders.
- `clientData.firstName` (string, optional) Example: `Anna`
- `clientData.lastName` (string, optional) Example: `Svensson`
- `clientData.companyName` (string, optional) Example: `Example AB`
- `clientData.email` (string, optional) Example: `user@example.com`
- `clientData.address` (object, optional)
- `clientData.address.street` (string, optional) Example: `Examplegatan 1`
- `clientData.address.address2` (string,null, optional) Example: `null`
- `clientData.address.city` (string, optional) Example: `Stockholm`
- `clientData.address.state` (string, optional) Example: `Stockholm`
- `clientData.address.postalCode` (string, optional) Example: `12345`
- `clientData.countryCode` (string, optional) Example: `SE`
- `clientData.phoneNumber` (string, optional) Example: `+46700000000`
- `clientData.registrationIdentifier` (object, optional)
- `clientData.registrationIdentifier.value` (string, optional) Example: `850507-3412`
- `clientData.registrationIdentifier.countryCode` (string,null, optional) Example: `SE`
- `clientData.vatNumber` (string, optional) Example: `SE559999999901`
- `clientData.accountType` (string, optional) Example: `private`
  Allowed values: private, organisation
- `paymentMethod` (string, optional): Canonical checkout preference for the invoice created by the order. Use `card` for card-backed hosted checkout flows, `swish` for Swish, `bankgiro` for Swedish SEK invoice payment, `sepa` for EUR SEPA bank transfer, or `invoice` to create the invoice without starting a specific payment flow. Provider names are not public v2 method values. Account credit is applied after invoice creation through the invoice credit action. Example: `card`
  Allowed values: card, swish, bankgiro, sepa, invoice
- `attemptKey` (string, optional): Optional caller-generated idempotency key for retry-safe order creation. Generate a fresh value for each logical order/checkout attempt and reuse that exact value only when retrying the same request after a timeout or lost response. If the previous attempt created an order, repeats with the same key within the duplicate-prevention window (currently one hour) return that existing order/invoice instead of creating another order. Do not reuse a key for a different cart. Example: `order_attempt_01hxa3b4c5d6e7f8g9h0j1k2m3`

### Request Examples

#### Register .se domain with stored profile

```bash
curl -X POST "https://cloud.hostup.se/api/v2/orders" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Accept: application/json" \
  -H "Content-Type: application/json" \
  -d '{
    "paymentMethod": "bankgiro",
    "items": [
      {
        "type": "domain",
        "action": "register",
        "domainName": "example.se",
        "years": 1,
        "acceptedTerms": [
          "se_registration_terms"
        ]
      }
    ]
  }'
```

```json
{
  "paymentMethod": "bankgiro",
  "items": [
    {
      "type": "domain",
      "action": "register",
      "domainName": "example.se",
      "years": 1,
      "acceptedTerms": [
        "se_registration_terms"
      ]
    }
  ]
}
```

#### Register .se and provide missing profile fields

```bash
curl -X POST "https://cloud.hostup.se/api/v2/orders" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Accept: application/json" \
  -H "Content-Type: application/json" \
  -d '{
    "paymentMethod": "bankgiro",
    "items": [
      {
        "type": "domain",
        "action": "register",
        "domainName": "example.se",
        "years": 1,
        "acceptedTerms": [
          "se_registration_terms"
        ]
      }
    ],
    "clientData": {
      "countryCode": "SE",
      "phoneNumber": "+46700000000",
      "accountType": "private",
      "registrationIdentifier": {
        "value": "850507-3412",
        "countryCode": "SE"
      }
    }
  }'
```

```json
{
  "paymentMethod": "bankgiro",
  "items": [
    {
      "type": "domain",
      "action": "register",
      "domainName": "example.se",
      "years": 1,
      "acceptedTerms": [
        "se_registration_terms"
      ]
    }
  ],
  "clientData": {
    "countryCode": "SE",
    "phoneNumber": "+46700000000",
    "accountType": "private",
    "registrationIdentifier": {
      "value": "850507-3412",
      "countryCode": "SE"
    }
  }
}
```

#### Register .se for a different registrant

```bash
curl -X POST "https://cloud.hostup.se/api/v2/orders" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Accept: application/json" \
  -H "Content-Type: application/json" \
  -d '{
    "paymentMethod": "bankgiro",
    "items": [
      {
        "type": "domain",
        "action": "register",
        "domainName": "example.se",
        "years": 1,
        "acceptedTerms": [
          "se_registration_terms"
        ],
        "registrantContact": {
          "type": "private",
          "firstName": "Anna",
          "lastName": "Svensson",
          "email": "user@example.com",
          "phoneNumber": "+46700000000",
          "street": "Examplegatan 1",
          "city": "Stockholm",
          "state": "Stockholm",
          "postalCode": "123 45",
          "countryCode": "SE",
          "registrationIdentifier": {
            "value": "850507-3412",
            "countryCode": "SE"
          }
        }
      }
    ]
  }'
```

```json
{
  "paymentMethod": "bankgiro",
  "items": [
    {
      "type": "domain",
      "action": "register",
      "domainName": "example.se",
      "years": 1,
      "acceptedTerms": [
        "se_registration_terms"
      ],
      "registrantContact": {
        "type": "private",
        "firstName": "Anna",
        "lastName": "Svensson",
        "email": "user@example.com",
        "phoneNumber": "+46700000000",
        "street": "Examplegatan 1",
        "city": "Stockholm",
        "state": "Stockholm",
        "postalCode": "123 45",
        "countryCode": "SE",
        "registrationIdentifier": {
          "value": "850507-3412",
          "countryCode": "SE"
        }
      }
    }
  ]
}
```

#### Order shared hosting for a domain

```bash
curl -X POST "https://cloud.hostup.se/api/v2/orders" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Accept: application/json" \
  -H "Content-Type: application/json" \
  -d '{
    "paymentMethod": "invoice",
    "items": [
      {
        "type": "shared_hosting",
        "productSlug": "webbhotell-start",
        "billingCycle": "annually",
        "primaryDomain": "example.com",
        "migration": {
          "type": "none"
        }
      }
    ]
  }'
```

```json
{
  "paymentMethod": "invoice",
  "items": [
    {
      "type": "shared_hosting",
      "productSlug": "webbhotell-start",
      "billingCycle": "annually",
      "primaryDomain": "example.com",
      "migration": {
        "type": "none"
      }
    }
  ]
}
```

#### Transfer a domain with EPP code

```bash
curl -X POST "https://cloud.hostup.se/api/v2/orders" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Accept: application/json" \
  -H "Content-Type: application/json" \
  -d '{
    "paymentMethod": "card",
    "items": [
      {
        "type": "domain",
        "action": "transfer",
        "domainName": "example.com",
        "years": 1,
        "eppCode": "YOUR_EPP_CODE"
      }
    ]
  }'
```

```json
{
  "paymentMethod": "card",
  "items": [
    {
      "type": "domain",
      "action": "transfer",
      "domainName": "example.com",
      "years": 1,
      "eppCode": "YOUR_EPP_CODE"
    }
  ]
}
```

#### Order a regular fixed-cycle VPS

```bash
curl -X POST "https://cloud.hostup.se/api/v2/orders" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Accept: application/json" \
  -H "Content-Type: application/json" \
  -d '{
    "paymentMethod": "invoice",
    "items": [
      {
        "type": "vps",
        "productSlug": "vps-xs",
        "billingCycle": "monthly",
        "os": "ubuntu-24-04",
        "customHostname": "app-01.example.com"
      }
    ]
  }'
```

```json
{
  "paymentMethod": "invoice",
  "items": [
    {
      "type": "vps",
      "productSlug": "vps-xs",
      "billingCycle": "monthly",
      "os": "ubuntu-24-04",
      "customHostname": "app-01.example.com"
    }
  ]
}
```

### Responses

#### 200 - Idempotent retry returned the existing order or account-only result.
```json
{
  "id": "ord_01hxa3b4c5d6e7f8g9h0j1k2m3",
  "number": "2096179503",
  "status": "pending",
  "type": "new",
  "invoiceId": "inv_01hxa3b4c5d6e7f8g9h0j1k2m3",
  "checkoutUrl": "https://cloud.hostup.se/billing?invoice=202600001",
  "client": {
    "id": "client_01hxa3b4c5d6e7f8g9h0j1k2m3",
    "email": null,
    "firstName": "Example",
    "lastName": "Customer",
    "companyName": "Example Company"
  },
  "billing": {
    "amount": 98.75,
    "currencyCode": "SEK",
    "billingCycle": null,
    "isPayg": false
  },
  "invoice": {
    "id": "inv_01hxa3b4c5d6e7f8g9h0j1k2m3",
    "number": "202600001",
    "amount": 98.75,
    "currencyCode": "SEK",
    "dueAt": "2026-05-11T23:59:59.000Z",
    "status": "unpaid",
    "paymentUrl": "/billing?invoice=202600001",
    "totals": {
      "currencyCode": "SEK",
      "total": 98.75,
      "amountPaid": 0,
      "outstanding": 98.75
    },
    "dates": {
      "dueAt": "2026-05-11T23:59:59.000Z"
    }
  },
  "paymentStatus": {
    "status": "unpaid",
    "reason": "Invoice has not been paid yet."
  },
  "actions": {
    "canRetry": {
      "allowed": false,
      "reason": "The order invoice must be paid before retrying."
    },
    "canCancel": {
      "allowed": true,
      "reason": null
    }
  },
  "domains": [
    {
      "name": "example.com",
      "tld": "se",
      "amount": 79,
      "currencyCode": "SEK"
    }
  ],
  "hosting": [
    {
      "name": "Example Web Hosting",
      "amount": 150,
      "currencyCode": "SEK"
    }
  ],
  "addons": [],
  "upgrades": [],
  "invoiceLookupPending": false,
  "createdAt": "2026-04-27T12:00:00.000Z",
  "contractAcceptedAt": null,
  "notes": null,
  "referenceNumber": null,
  "pendingManualProcessing": false,
  "enableAffiliateTracking": true
}
```

#### 201 - Order created.
```json
{
  "id": "ord_01hxa3b4c5d6e7f8g9h0j1k2m3",
  "number": "2096179503",
  "status": "pending",
  "type": "new",
  "invoiceId": "inv_01hxa3b4c5d6e7f8g9h0j1k2m3",
  "checkoutUrl": "https://cloud.hostup.se/billing?invoice=202600001",
  "client": {
    "id": "client_01hxa3b4c5d6e7f8g9h0j1k2m3",
    "email": null,
    "firstName": "Example",
    "lastName": "Customer",
    "companyName": "Example Company"
  },
  "billing": {
    "amount": 98.75,
    "currencyCode": "SEK",
    "billingCycle": null,
    "isPayg": false
  },
  "invoice": {
    "id": "inv_01hxa3b4c5d6e7f8g9h0j1k2m3",
    "number": "202600001",
    "amount": 98.75,
    "currencyCode": "SEK",
    "dueAt": "2026-05-11T23:59:59.000Z",
    "status": "unpaid",
    "paymentUrl": "/billing?invoice=202600001",
    "totals": {
      "currencyCode": "SEK",
      "total": 98.75,
      "amountPaid": 0,
      "outstanding": 98.75
    },
    "dates": {
      "dueAt": "2026-05-11T23:59:59.000Z"
    }
  },
  "paymentStatus": {
    "status": "unpaid",
    "reason": "Invoice has not been paid yet."
  },
  "actions": {
    "canRetry": {
      "allowed": false,
      "reason": "The order invoice must be paid before retrying."
    },
    "canCancel": {
      "allowed": true,
      "reason": null
    }
  },
  "domains": [
    {
      "name": "example.com",
      "tld": "se",
      "amount": 79,
      "currencyCode": "SEK"
    }
  ],
  "hosting": [
    {
      "name": "Example Web Hosting",
      "amount": 150,
      "currencyCode": "SEK"
    }
  ],
  "addons": [],
  "upgrades": [],
  "invoiceLookupPending": false,
  "createdAt": "2026-04-27T12:00:00.000Z",
  "contractAcceptedAt": null,
  "notes": null,
  "referenceNumber": null,
  "pendingManualProcessing": false,
  "enableAffiliateTracking": true
}
```

#### 400 - Invalid request. The response body is an RFC 7807 Problem Details document.
```json
{
  "type": "https://developer.hostup.se/errors/invalid_request",
  "title": "Invalid request",
  "status": 400,
  "detail": "The request body failed validation.",
  "code": "invalid_request",
  "instance": "/api/v2/resource",
  "requestId": "req_01hxa3b4c5d6e7f8g9h0j1k2m3",
  "timestamp": "2026-04-27T12:34:56.000Z",
  "errors": [
    {
      "pointer": "/items/0/domainName",
      "detail": "`domainName` is required.",
      "code": "invalid_request"
    }
  ]
}
```

#### 401 - Unauthorized. Authentication is required.
```json
{
  "type": "https://developer.hostup.se/errors/unauthorized",
  "title": "Unauthorized",
  "status": 401,
  "detail": "Authentication is required.",
  "code": "unauthorized",
  "instance": "/api/v2/resource",
  "requestId": "req_01hxa3b4c5d6e7f8g9h0j1k2m3",
  "timestamp": "2026-04-27T12:34:56.000Z"
}
```

#### 403 - Forbidden. The caller lacks a required scope or does not own the resource.
```json
{
  "type": "https://developer.hostup.se/errors/forbidden",
  "title": "Forbidden",
  "status": 403,
  "detail": "The caller lacks a required scope or does not own the resource.",
  "code": "forbidden",
  "instance": "/api/v2/resource",
  "requestId": "req_01hxa3b4c5d6e7f8g9h0j1k2m3",
  "timestamp": "2026-04-27T12:34:56.000Z"
}
```

#### 404 - Not found. The resource does not exist or is not owned by the caller.
```json
{
  "type": "https://developer.hostup.se/errors/not_found",
  "title": "Not found",
  "status": 404,
  "detail": "The requested resource could not be found.",
  "code": "not_found",
  "instance": "/api/v2/resource",
  "requestId": "req_01hxa3b4c5d6e7f8g9h0j1k2m3",
  "timestamp": "2026-04-27T12:34:56.000Z"
}
```

#### 429 - Rate limited. Retry after the limit resets. 429 responses include `Retry-After` seconds plus `X-RateLimit-*` headers.
```json
{
  "type": "https://developer.hostup.se/errors/rate_limit_exceeded",
  "title": "Too many requests",
  "status": 429,
  "detail": "Too many requests. Retry after the limit resets.",
  "code": "rate_limit_exceeded",
  "instance": "/api/v2/resource",
  "requestId": "req_01hxa3b4c5d6e7f8g9h0j1k2m3",
  "timestamp": "2026-04-27T12:34:56.000Z"
}
```

#### 500 - Internal error. Retry later or contact support if the issue persists.
```json
{
  "type": "https://developer.hostup.se/errors/internal_error",
  "title": "Internal server error",
  "status": 500,
  "detail": "An unexpected error occurred. Retry later or contact support if the issue persists.",
  "code": "internal_error",
  "instance": "/api/v2/resource",
  "requestId": "req_01hxa3b4c5d6e7f8g9h0j1k2m3",
  "timestamp": "2026-04-27T12:34:56.000Z"
}
```
