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

# Create a customer portal session

> Creates a signed, time-limited portal link for one customer, identified by their external customer id. Hand the returned `url` to the customer so they can view their own invoices without your API key ever reaching them. The session expires after one hour; create a fresh one when it does.



## OpenAPI

````yaml /api-reference/openapi.json post /v1/customer_portal/sessions
openapi: 3.1.0
info:
  title: Finseed API
  version: 1.0.0
  description: >-
    REST API for programmatic Verifactu invoicing. Every request is sent over
    HTTPS and authenticated with an API key passed as a bearer token. Each key
    is scoped to a single environment (test or live). Payloads are JSON with
    snake_case fields: amounts are integer minor units (cents) and timestamps
    are RFC 3339 in UTC. List endpoints are cursor-paginated. Errors return a
    small application/json envelope with a stable, machine-readable `code`.
servers:
  - url: https://api.finseed.es
security:
  - bearerAuth: []
paths:
  /v1/customer_portal/sessions:
    post:
      tags:
        - Customer portal
      summary: Create a customer portal session
      description: >-
        Creates a signed, time-limited portal link for one customer, identified
        by their external customer id. Hand the returned `url` to the customer
        so they can view their own invoices without your API key ever reaching
        them. The session expires after one hour; create a fresh one when it
        does.
      operationId: createCustomerPortalSession
      requestBody:
        description: The customer to create a portal session for.
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CustomerPortalSessionRequest'
      responses:
        '200':
          description: A signed link to a customer portal session.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/CustomerPortalSession'
        '400':
          description: >-
            The request was rejected during validation. The `errors` array lists
            each failing field with a reason, so you can map the failure back to
            a specific parameter.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorEnvelope'
              example:
                code: invalid_request
                detail: >-
                  The request is not valid. A parameter is missing, malformed,
                  or out of range. See the `errors` array for the fields that
                  failed.
                errors: []
                doc_url: >-
                  https://www.finseed.es/docs/api-reference/errors#invalid_request
        '401':
          description: >-
            Authentication failed: the API key is missing, malformed, or
            revoked. Send a valid key as a bearer token in the `Authorization`
            header. Each key is bound to a single environment (test or live).
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorEnvelope'
              example:
                code: unauthorized
                detail: >-
                  Authentication failed. Send your API key as a bearer token in
                  the `Authorization` header. Keys are scoped to one
                  environment, so a test key cannot read live data and vice
                  versa.
                errors: []
                doc_url: https://www.finseed.es/docs/api-reference/errors#unauthorized
        '409':
          description: >-
            The environment has more than one integration, so
            `external_customer_id` on its own does not identify a single
            customer (the same id can exist in two integrations for different
            customers). Supply `integration_id` alongside `external_customer_id`
            to scope the session to one integration. You do not need it when the
            environment has a single integration. Find the id in the Finseed app
            under Configuración > Integraciones: open the integration and copy
            the "ID de integración" shown below its name. See [Create a customer
            portal
            session](/api-reference/customer-portal/create-a-customer-portal-session#body-integration-id)
            for the full parameter reference.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorEnvelope'
              example:
                code: customer_ambiguous
                detail: >-
                  This environment has more than one integration, so an external
                  customer id alone is ambiguous: the same id can refer to
                  different customers in different integrations. Supply
                  `integration_id` to indicate which integration the customer
                  belongs to.
                errors: []
                doc_url: >-
                  https://www.finseed.es/docs/api-reference/errors#customer_ambiguous
        '500':
          description: >-
            An unexpected error occurred on our side. No partial work is
            persisted, so the request can be retried safely.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorEnvelope'
              example:
                code: internal_error
                detail: >-
                  Something went wrong on our side. The request did not complete
                  and can be retried safely.
                errors: []
                doc_url: >-
                  https://www.finseed.es/docs/api-reference/errors#internal_error
components:
  schemas:
    CustomerPortalSessionRequest:
      type: object
      properties:
        external_customer_id:
          type: string
          minLength: 1
          description: >-
            External customer identifier from the originating integration (for
            example the Stripe customer id). The portal shows the invoices that
            belong to this customer.
          example: cus_9aZ
        integration_id:
          description: >-
            The integration the customer belongs to. Required only when your
            environment has more than one integration, because the same external
            customer id can refer to different customers across integrations;
            omit it when you have a single integration. If it is required but
            missing, the request fails with `customer_ambiguous`. Find it in the
            Finseed app under Configuración > Integraciones: open the
            integration and copy the "ID de integración" shown below its name.
          example: int_3kQ
          type: string
          minLength: 1
      required:
        - external_customer_id
      additionalProperties: false
      description: Identifies the customer a portal session is created for.
      example:
        external_customer_id: cus_9aZ
    CustomerPortalSession:
      type: object
      properties:
        url:
          type: string
          description: >-
            Portal URL carrying a signed, time-limited session token. Hand it to
            the customer so they can view their invoices without exposing your
            API key.
          example: https://www.finseed.es/app/customer-portal/eyJ…
        expires_at:
          type: string
          description: >-
            When the portal session stops working, RFC 3339 in UTC. Create a
            fresh session after this.
          example: '2026-06-01T10:30:00Z'
      required:
        - url
        - expires_at
      additionalProperties: false
      description: A signed link to a customer portal session.
      example:
        url: https://www.finseed.es/app/customer-portal/eyJ…
        expires_at: '2026-06-01T10:30:00Z'
    ErrorEnvelope:
      type: object
      properties:
        code:
          type: string
          description: >-
            Stable, machine-readable error code. The branch key. This is an
            extensible enum: new values may be added in the future, so clients
            must tolerate values not listed here. Known values: invalid_request,
            unauthorized, invoice_not_found, download_link_expired,
            number_series_not_found, number_series_prefix_taken,
            number_series_in_use, customer_not_found, customer_ambiguous,
            idempotency_conflict, rate_limited, invalid_number_series,
            invalid_tax_rate, invalid_tax_data, invalid_tax_id,
            tax_id_type_country_mismatch, insufficient_recipient_data,
            simplified_amount_exceeded, simplified_incompatible_tax_type,
            invalid_recargo_equivalencia, aeat_mandate_not_approved,
            no_active_subscription, tax_id_validation_unavailable,
            invalid_rectifies_reference, rectified_invoice_not_found,
            invoice_already_voided, invoice_already_replaced,
            invoice_not_family_tip, invoice_pending_at_aeat,
            invoice_not_rectifiable_at_aeat, family_balance_below_zero,
            replacement_total_negative, reason_incompatible_with_recipient,
            recipient_overrides_not_allowed_for_differences,
            rectifying_series_invalid, external_reference_invalid,
            external_reference_matches_existing_invoice,
            rectified_amounts_required_for_substitution,
            line_item_without_tax_items, internal_error.
          example: invalid_request
        detail:
          type: string
          description: Human-readable, context-specific explanation.
          example: The query parameters are not valid.
        errors:
          type: array
          items:
            type: object
            properties:
              field:
                type: string
                description: Location of the offending value (JSON path).
                example: limit
              code:
                type: string
                description: Machine-readable validation issue code.
                example: too_big
              message:
                type: string
                description: Human-readable explanation of the field-level issue.
                example: Number must be less than or equal to 100
            required:
              - field
              - code
              - message
            additionalProperties: false
          description: >-
            Field-level issues. Always present; empty unless this is a
            validation failure.
        doc_url:
          type: string
          description: Link to the reference documentation for this error code.
          example: https://www.finseed.es/docs/api-reference/errors#invalid_request
      required:
        - code
        - detail
        - errors
        - doc_url
      additionalProperties: false
      description: >-
        Uniform error body returned for every non-2xx response
        (application/json).
  securitySchemes:
    bearerAuth:
      type: http
      scheme: bearer
      description: API key sent as a bearer token in the `Authorization` header.

````