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

> ## Agent Instructions
> Taberna is a crypto payments API. Amounts are priced in fiat (USD or EUR) and paid in crypto.
> Fiat amounts are ALWAYS decimal strings ("9.99"), never numbers. Crypto amounts are integer strings in the asset's smallest unit.
> Authenticate merchant endpoints with `Authorization: Bearer tbrn_live_...`. Every key is bound to one store and carries an explicit scope set, so never pass a store id to a /v1 endpoint.
> A 401 means the credential is bad; a 403 means the key lacks a scope. Never retry or re-authenticate on a 403 — surface it as a configuration error.
> The /v1/checkouts endpoints are deliberately unauthenticated and run in the buyer's browser. Never send an API key to a browser.
> Verify webhook signatures over the RAW request body before parsing JSON, and treat delivery as at-least-once: handlers must be idempotent.
> Never grant value based on a browser redirect to successUrl. Fulfil on a signature-verified webhook, or on a server-side read of GET /v1/orders/{id} or GET /v1/invoices/{id}.
> There is no test mode: every payment method is a live chain moving real funds.

# Create an invoice

> Issues an open invoice and returns it, including the hosted payment `url`. Requires the `invoices:write` scope.

Supply `items` directly, or a `templateId` to take the items, memo and expiry from a saved template — anything sent explicitly replaces the template default wholesale. Creating an invoice never emails anyone; use the send endpoint for that.



## OpenAPI

````yaml /api-reference/openapi.json post /v1/invoices
openapi: 3.1.0
info:
  title: Taberna API
  description: >-
    Accept crypto payments for digital goods. Create orders and invoices, hand
    the buyer a hosted checkout link, and let webhooks tell you when the money
    lands.


    Authenticate merchant endpoints with a store API key as a bearer token
    (`Authorization: Bearer tbrn_live_…`). Every key is bound to one store and
    carries an explicit set of scopes; the scope a route requires is stated in
    its description and in the `x-required-scope` extension. A key without the
    scope gets a 403 — mint one with the scope rather than retrying.


    The Checkout endpoints are the exception: they are unauthenticated and meant
    to be called from the buyer's browser. Possession of the checkout id is the
    credential there, so never ship an API key to a page.


    Conventions: ids in links are short ids; fiat amounts are decimal strings
    ("9.99"); crypto amounts are integer strings in the asset's smallest unit;
    timestamps are ISO-8601 UTC. Errors carry `{ "error": "…" }` unless
    documented otherwise.
  version: 1.0.0
servers:
  - url: https://api.taberna.io
    description: Taberna API
security: []
tags:
  - name: Orders
    description: Carts of products, paid through a hosted checkout.
  - name: Invoices
    description: Merchant-issued bills with their own payment page.
  - name: Invoice Templates
    description: Saved invoice presets, referenced by `templateId` when creating one.
  - name: Products
    description: Catalogue reads and delivery stock top-ups.
  - name: Store
    description: What the calling API key is and what it may do.
  - name: Webhook Deliveries
    description: Delivery history for the store webhook, and replay.
  - name: Checkout
    description: >-
      Buyer-facing hosted-page endpoints. Unauthenticated by design — never send
      an API key.
paths:
  /v1/invoices:
    post:
      tags:
        - Invoices
      summary: Create an invoice
      description: >-
        Issues an open invoice and returns it, including the hosted payment
        `url`. Requires the `invoices:write` scope.


        Supply `items` directly, or a `templateId` to take the items, memo and
        expiry from a saved template — anything sent explicitly replaces the
        template default wholesale. Creating an invoice never emails anyone; use
        the send endpoint for that.
      operationId: postV1Invoices
      parameters:
        - in: header
          name: Idempotency-Key
          required: false
          description: >-
            Optional replay guard, 1–255 characters. Retrying a create with a
            key already used by this store returns the original resource instead
            of making a second one.
          schema:
            type: string
            minLength: 1
            maxLength: 255
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                items:
                  minItems: 1
                  maxItems: 100
                  type: array
                  items:
                    type: object
                    properties:
                      name:
                        type: string
                        minLength: 1
                        maxLength: 255
                      description:
                        type: string
                        maxLength: 2000
                      quantity:
                        type: integer
                        exclusiveMinimum: 0
                        maximum: 1000000
                      unitAmount:
                        type: string
                        pattern: ^\d+(\.\d{1,3})?$
                    required:
                      - name
                      - quantity
                      - unitAmount
                templateId:
                  type: string
                  format: uuid
                  pattern: >-
                    ^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$
                currency:
                  type: string
                  enum:
                    - usd
                    - eur
                customerName:
                  type: string
                  maxLength: 255
                customerEmail:
                  type: string
                  format: email
                  pattern: >-
                    ^(?!\.)(?!.*\.\.)([A-Za-z0-9_'+\-\.]*)[A-Za-z0-9_+-]@([A-Za-z0-9][A-Za-z0-9\-]*\.)+[A-Za-z]{2,}$
                memo:
                  type: string
                  maxLength: 5000
                metadata:
                  type: object
                  propertyNames:
                    type: string
                  additionalProperties: {}
                successUrl:
                  type: string
                  maxLength: 2048
                  format: uri
                cancelUrl:
                  type: string
                  maxLength: 2048
                  format: uri
                expiresIn:
                  type: integer
                  minimum: 10
                  maximum: 129600
      responses:
        '201':
          description: >-
            The invoice. A replayed `Idempotency-Key` returns the original
            invoice with this same status.
          content:
            application/json:
              schema:
                type: object
                properties:
                  id:
                    type: string
                    description: >-
                      Public short id — the value that appears in checkout
                      links.
                  number:
                    type: integer
                    minimum: -9007199254740991
                    maximum: 9007199254740991
                  status:
                    type: string
                    enum:
                      - open
                      - paid
                      - partial
                      - expired
                      - voided
                  url:
                    type: string
                  items:
                    type: array
                    items:
                      type: object
                      properties:
                        name:
                          type: string
                        description:
                          anyOf:
                            - type: string
                            - type: 'null'
                        quantity:
                          type: integer
                          minimum: -9007199254740991
                          maximum: 9007199254740991
                        unitAmount:
                          type: string
                          description: >-
                            Fiat amount as a decimal string, e.g. "9.99". Never
                            a number.
                        amount:
                          type: string
                          description: >-
                            Fiat amount as a decimal string, e.g. "9.99". Never
                            a number.
                      required:
                        - name
                        - description
                        - quantity
                        - unitAmount
                        - amount
                  totalAmount:
                    type: string
                    description: >-
                      Fiat amount as a decimal string, e.g. "9.99". Never a
                      number.
                  currency:
                    type: string
                    enum:
                      - usd
                      - eur
                  customerName:
                    anyOf:
                      - type: string
                      - type: 'null'
                  customerEmail:
                    anyOf:
                      - type: string
                      - type: 'null'
                  memo:
                    anyOf:
                      - type: string
                      - type: 'null'
                  metadata:
                    anyOf:
                      - type: object
                        propertyNames:
                          type: string
                        additionalProperties: {}
                      - type: 'null'
                  successUrl:
                    anyOf:
                      - type: string
                      - type: 'null'
                  cancelUrl:
                    anyOf:
                      - type: string
                      - type: 'null'
                  createdVia:
                    type: string
                    enum:
                      - api
                      - dashboard
                  expiresAt:
                    type: string
                    format: date-time
                    pattern: >-
                      ^(?:(?:\d\d[2468][048]|\d\d[13579][26]|\d\d0[48]|[02468][048]00|[13579][26]00)-02-29|\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\d|30)|(?:02)-(?:0[1-9]|1\d|2[0-8])))T(?:(?:[01]\d|2[0-3]):[0-5]\d(?::[0-5]\d(?:\.\d+)?)?(?:Z))$
                    description: ISO-8601 timestamp in UTC.
                  paidAt:
                    anyOf:
                      - type: string
                        format: date-time
                        pattern: >-
                          ^(?:(?:\d\d[2468][048]|\d\d[13579][26]|\d\d0[48]|[02468][048]00|[13579][26]00)-02-29|\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\d|30)|(?:02)-(?:0[1-9]|1\d|2[0-8])))T(?:(?:[01]\d|2[0-3]):[0-5]\d(?::[0-5]\d(?:\.\d+)?)?(?:Z))$
                        description: ISO-8601 timestamp in UTC.
                      - type: 'null'
                  voidedAt:
                    anyOf:
                      - type: string
                        format: date-time
                        pattern: >-
                          ^(?:(?:\d\d[2468][048]|\d\d[13579][26]|\d\d0[48]|[02468][048]00|[13579][26]00)-02-29|\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\d|30)|(?:02)-(?:0[1-9]|1\d|2[0-8])))T(?:(?:[01]\d|2[0-3]):[0-5]\d(?::[0-5]\d(?:\.\d+)?)?(?:Z))$
                        description: ISO-8601 timestamp in UTC.
                      - type: 'null'
                  createdAt:
                    type: string
                    format: date-time
                    pattern: >-
                      ^(?:(?:\d\d[2468][048]|\d\d[13579][26]|\d\d0[48]|[02468][048]00|[13579][26]00)-02-29|\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\d|30)|(?:02)-(?:0[1-9]|1\d|2[0-8])))T(?:(?:[01]\d|2[0-3]):[0-5]\d(?::[0-5]\d(?:\.\d+)?)?(?:Z))$
                    description: ISO-8601 timestamp in UTC.
                  email:
                    type: object
                    properties:
                      sendCount:
                        type: integer
                        minimum: -9007199254740991
                        maximum: 9007199254740991
                      sendsRemaining:
                        type: integer
                        minimum: -9007199254740991
                        maximum: 9007199254740991
                      maxSends:
                        type: integer
                        minimum: -9007199254740991
                        maximum: 9007199254740991
                      lastSentAt:
                        anyOf:
                          - type: string
                            format: date-time
                            pattern: >-
                              ^(?:(?:\d\d[2468][048]|\d\d[13579][26]|\d\d0[48]|[02468][048]00|[13579][26]00)-02-29|\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\d|30)|(?:02)-(?:0[1-9]|1\d|2[0-8])))T(?:(?:[01]\d|2[0-3]):[0-5]\d(?::[0-5]\d(?:\.\d+)?)?(?:Z))$
                            description: ISO-8601 timestamp in UTC.
                          - type: 'null'
                      cooldownUntil:
                        anyOf:
                          - type: string
                            format: date-time
                            pattern: >-
                              ^(?:(?:\d\d[2468][048]|\d\d[13579][26]|\d\d0[48]|[02468][048]00|[13579][26]00)-02-29|\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\d|30)|(?:02)-(?:0[1-9]|1\d|2[0-8])))T(?:(?:[01]\d|2[0-3]):[0-5]\d(?::[0-5]\d(?:\.\d+)?)?(?:Z))$
                            description: ISO-8601 timestamp in UTC.
                          - type: 'null'
                      canSend:
                        type: boolean
                      blockedReason:
                        anyOf:
                          - type: string
                          - type: 'null'
                    required:
                      - sendCount
                      - sendsRemaining
                      - maxSends
                      - lastSentAt
                      - cooldownUntil
                      - canSend
                      - blockedReason
                required:
                  - id
                  - number
                  - status
                  - url
                  - items
                  - totalAmount
                  - currency
                  - customerName
                  - customerEmail
                  - memo
                  - metadata
                  - successUrl
                  - cancelUrl
                  - createdVia
                  - expiresAt
                  - paidAt
                  - voidedAt
                  - createdAt
                  - email
        '400':
          description: >-
            The request failed validation (`{ "error": "Invalid request data"
            }`), or the operation is not allowed in the resource's current
            state.
          content:
            application/json:
              schema:
                type: object
                properties:
                  error:
                    type: string
                required:
                  - error
                description: Error response.
        '401':
          description: >-
            Missing, malformed, revoked or expired API key. The
            `WWW-Authenticate` header names the challenge that failed.
          content:
            application/json:
              schema:
                type: object
                properties:
                  error:
                    type: string
                required:
                  - error
                description: Error response.
        '403':
          description: The key is valid but was not granted the scope this route requires.
          content:
            application/json:
              schema:
                type: object
                properties:
                  error:
                    type: string
                required:
                  - error
                description: Error response.
      security:
        - bearerAuth: []
components:
  securitySchemes:
    bearerAuth:
      type: http
      scheme: bearer
      description: >-
        A store API key, sent as `Authorization: Bearer <key>`. Live keys are
        prefixed `tbrn_live_`. Keys are minted per store in the dashboard with
        an explicit scope set.

````