## POST /api/v2/billing/invoices/{id}/actions/reissue-in-currency

**Reissue invoice in EUR for bank transfer**

Switch the billing account to EUR and replace the open service renewal invoices with EUR invoices for the same periods, so a customer outside Sweden can pay by SEPA bank transfer instead of a Swedish bankgiro. Read the invoice first: `paymentMethods.sepa.visible` is true and `available` is false when bank transfer exists one currency away, and `actions.canReissueInCurrency.allowed` says whether this action will succeed for that invoice. Every open renewal invoice on the account is replaced in the same call because an account holds one currency; send `dryRun: true` to get the full plan (affected invoices, expected EUR amounts and due dates, blockers) without changing anything. The real call is synchronous: the account currency changes first, then each invoice is cancelled and replaced; the response lists every cancelled/replacement pair. Only `EUR` is offered. Get `{id}` from `GET /api/v2/billing/invoices` `data[].id` or `GET /api/v2/billing/invoices/{id}` `id`.

### Related Endpoints

- `POST /api/v2/billing/invoices/{id}/actions/pay`: Pay invoice by saved card
- `POST /api/v2/billing/invoices/{id}/actions/cancel`: Cancel invoice
- `POST /api/v2/billing/invoices/{id}/actions/apply-credit`: Apply account credit to invoice

### Headers

- `Accept`: application/json
- `Authorization`: Bearer YOUR_API_KEY
- Required API scopes: `write:billing`, `write:account`
- `Content-Type`: application/json

### Parameters

- `id` (path, string, required): Public invoice ID from invoice list/detail. Do not invent this value; use the exact ID returned by the referenced API response. Example: `inv_01hxa3b4c5d6e7f8g9h0j1k2m3`

### Request Body

- `currencyCode` (string, required): Target currency. Only EUR is offered: it is the currency with a bank-transfer rail (SEPA) for customers outside Sweden.
  Allowed values: EUR
- `dryRun` (boolean, optional): When true, return the plan without changing the account or any invoice.

### Request Examples

#### Preview the switch

```bash
curl -X POST "https://cloud.hostup.se/api/v2/billing/invoices/inv_01hxa3b4c5d6e7f8g9h0j1k2m3/actions/reissue-in-currency" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Accept: application/json" \
  -H "Content-Type: application/json" \
  -d '{
    "currencyCode": "EUR",
    "dryRun": true
  }'
```

```json
{
  "currencyCode": "EUR",
  "dryRun": true
}
```

#### Switch to EUR and reissue

```bash
curl -X POST "https://cloud.hostup.se/api/v2/billing/invoices/inv_01hxa3b4c5d6e7f8g9h0j1k2m3/actions/reissue-in-currency" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Accept: application/json" \
  -H "Content-Type: application/json" \
  -d '{
    "currencyCode": "EUR"
  }'
```

```json
{
  "currencyCode": "EUR"
}
```

### Response Schema

- `currencyCode` (string, required) Example: `EUR`
- `exchangeRate` (number, required, nullable): Target currency units per one SEK, as configured in billing. The replacement amount is the product's price in the new currency; `expectedAmount` is the rate-converted expectation. Example: `0.1`
- `allowed` (boolean, required): True when the real call would succeed for every affected invoice.
- `blockers` (array<object>, required)
- `blockers[].invoiceId` (string, required, nullable): Nullable (may be null when not applicable).
- `blockers[].invoiceNumber` (string, required, nullable): Nullable (may be null when not applicable).
- `blockers[].code` (string, required)
  Allowed values: account_country_not_eligible, invoice_not_unpaid, invoice_already_in_currency, invoice_partially_settled, invoice_peppol_delivered, invoice_order_backed, invoice_add_funds, invoice_usage_not_reissuable, invoice_domain_renewal, invoice_not_service_renewal, invoice_multiple_services, renewal_period_ended, currency_not_supported, pending_orders, rate_limited
- `blockers[].reason` (string, required)
- `changeQuota` (object, required)
- `changeQuota.remaining` (integer, required): Currency changes left this period; the switch uses one.
- `changeQuota.resetAt` (string, required)
- `invoices` (array<object>, required): Every open renewal invoice the switch affects, the requested one first.
- `invoices[].id` (string, required)
- `invoices[].number` (string, required, nullable): Nullable (may be null when not applicable).
- `invoices[].amount` (number, required)
- `invoices[].currencyCode` (string, required)
- `invoices[].dueAt` (string, required, nullable): Nullable (may be null when not applicable).
- `invoices[].expectedAmount` (number, required, nullable): Nullable (may be null when not applicable).
- `invoices[].expectedCurrencyCode` (string, required)
- `invoices[].expectedDueAt` (string, required, nullable): The later of the original due date and seven days from today, so the transfer can arrive.
- `invoices[].actions` (object, required)
- `invoices[].actions.canReissueInCurrency` (object, required)
- `invoices[].actions.canReissueInCurrency.allowed` (boolean, required) Example: `true`
- `invoices[].actions.canReissueInCurrency.reason` (string, required, nullable): Nullable (may be null when not applicable). Example: `null`
- `invoices[].actions.canReissueInCurrency.code` (string, optional, nullable): Machine-readable reason code when an action is blocked. Example: `pending_order`

### Responses

#### 200 - With `dryRun: true`: the plan. Otherwise: the cancelled/replacement invoice pairs after the account switched to EUR.
```json
{
  "currencyCode": "EUR",
  "exchangeRate": 0.1,
  "allowed": true,
  "blockers": [],
  "changeQuota": {
    "remaining": 2,
    "resetAt": "2026-10-01T00:00:00.000Z"
  },
  "invoices": [
    {
      "id": "inv_01hxa3b4c5d6e7f8g9h0j1k2m3",
      "number": "202681499",
      "amount": 708,
      "currencyCode": "SEK",
      "dueAt": "2026-09-10T00:00:00.000Z",
      "expectedAmount": 70.8,
      "expectedCurrencyCode": "EUR",
      "expectedDueAt": "2026-09-17T00:00:00.000Z",
      "actions": {
        "canReissueInCurrency": {
          "allowed": true,
          "reason": null,
          "code": null
        }
      }
    }
  ]
}
```

#### 400 - Invalid body. `currencyCode` must be `EUR`; `dryRun` must be a boolean; no other fields are accepted.
```json
{
  "type": "https://developer.hostup.se/errors/invalid_request",
  "title": "Invalid request",
  "status": 400,
  "detail": "Bank transfer invoices are issued in EUR; currencyCode must be \"EUR\".",
  "code": "invalid_request",
  "instance": "/api/v2/billing/invoices/inv_01hxa3b4c5d6e7f8g9h0j1k2m3/actions/reissue-in-currency",
  "errors": [
    {
      "pointer": "/currencyCode",
      "detail": "Bank transfer invoices are issued in EUR; currencyCode must be \"EUR\".",
      "code": "invalid_currency"
    }
  ]
}
```

#### 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 - Delegated-access users cannot change the account currency (`delegated_access_forbidden`).
```json
{
  "type": "https://developer.hostup.se/errors/delegated_access_forbidden",
  "title": "Forbidden",
  "status": 403,
  "detail": "Delegated access users cannot change account currency.",
  "code": "delegated_access_forbidden",
  "instance": "/api/v2/billing/invoices/inv_01hxa3b4c5d6e7f8g9h0j1k2m3/actions/reissue-in-currency"
}
```

#### 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"
}
```

#### 409 - Blocked. `invoice_reissue_blocked` carries a `blockers` array (same shape as the preview) naming every reason; `pending_orders` when an open order must be finished or cancelled first.
```json
{
  "type": "https://developer.hostup.se/errors/invoice_reissue_blocked",
  "title": "Invoice cannot be reissued",
  "status": 409,
  "detail": "This invoice belongs to a pending order or plan change. Cancel that order and place it again after changing your account currency.",
  "code": "invoice_reissue_blocked",
  "instance": "/api/v2/billing/invoices/inv_01hxa3b4c5d6e7f8g9h0j1k2m3/actions/reissue-in-currency",
  "blockers": [
    {
      "invoiceId": "inv_01hxa3b4c5d6e7f8g9h0j1k2m3",
      "invoiceNumber": "202681499",
      "code": "invoice_order_backed",
      "reason": "This invoice belongs to a pending order or plan change. Cancel that order and place it again after changing your account currency."
    }
  ]
}
```

#### 429 - The monthly limit for currency changes is reached (`rate_limited`); `Retry-After` names the wait in seconds when known.
```json
{
  "type": "https://developer.hostup.se/errors/rate_limited",
  "title": "Rate limited",
  "status": 429,
  "detail": "Currency change limit reached (2/2 this period).",
  "code": "rate_limited",
  "instance": "/api/v2/billing/invoices/inv_01hxa3b4c5d6e7f8g9h0j1k2m3/actions/reissue-in-currency"
}
```

#### 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"
}
```

#### 502 - `upstream_failed` when the currency settings or the currency change itself failed (nothing changed). `upstream_renewal_regeneration_failed` when the account currency changed but at least one invoice could not be replaced: the response carries `invoices` with per-invoice `status`/`reason`; the original invoice stays payable by card and staff are notified.
```json
{
  "type": "https://developer.hostup.se/errors/upstream_renewal_regeneration_failed",
  "title": "Replacement invoice could not be issued",
  "status": 502,
  "detail": "Your account currency was updated, but at least one invoice could not be replaced. Our team has been notified; the original invoice remains payable by card.",
  "code": "upstream_renewal_regeneration_failed",
  "instance": "/api/v2/billing/invoices/inv_01hxa3b4c5d6e7f8g9h0j1k2m3/actions/reissue-in-currency",
  "currencyCode": "EUR",
  "changedCurrency": true,
  "invoices": [
    {
      "cancelledInvoice": {
        "id": "inv_01hxa3b4c5d6e7f8g9h0j1k2m3",
        "number": "202681499"
      },
      "cancelled": true,
      "replacementInvoice": null,
      "status": "failed",
      "reason": "The renewal invoice could not be generated"
    }
  ]
}
```
