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

# Link a Stripe customer

> Link a customer to the Stripe customer you already bill them with.

Use this when you move existing Stripe billing to Ledger: it's the first step of [Migrate from Stripe Billing](https://docs.integrateledger.com/migrate-from-stripe.md). Pass the exact `customerId` as a URL-encoded query value and the customer's `stripeCustomerId` from your Stripe account. Create the customer first with [Create or get a customer](https://docs.integrateledger.com/api-reference/customers/create.md).

Ledger checks that the Stripe customer exists in the Stripe account connected for the key's environment (Sandbox or Live), adds `ledger_organization_id`, `ledger_customer_id` and `ledger_env` to its metadata, and saves the link. It changes nothing else on the Stripe customer and charges nothing. After linking, Checkout and upgrades use this Stripe customer and its saved cards. A Sandbox customer on a test clock brings the clock's time with it.

Linking the same Stripe customer again changes nothing. The call returns `409 stripe_customer_already_linked` when the customer is linked to a different Stripe customer, `409 stripe_customer_linked_elsewhere` when another of your customers has this one, and `409 stripe_customer_owned_elsewhere` when its metadata names another Ledger workspace or environment. Unknown customers return `404 customer_not_found`; a Stripe customer that doesn't exist or was deleted returns `404 stripe_customer_not_found`. The key needs the `customers:write` permission.


## OpenAPI

````yaml openapi.json POST /v1/customers/stripe-customer
openapi: 3.1.0
info:
  title: Ledger API
  version: 0.1.0
servers:
  - url: https://api.integrateledger.com
    description: Hosted Ledger API
security: []
paths:
  /v1/customers/stripe-customer:
    post:
      tags:
        - Customers
      summary: Link a customer to its existing Stripe customer
      description: >-
        Links a customer to its existing Stripe customer, for a customer who was
        already billed in your Stripe account. Ledger adds its metadata to the
        Stripe customer and leaves everything else as it is. Later Checkouts and
        upgrades use this Stripe customer and its saved cards; import its
        subscription with POST /v1/billing/import. Linking the same Stripe
        customer again changes nothing.
      operationId: linkStripeCustomer
      parameters:
        - schema:
            type: string
            minLength: 1
            maxLength: 255
          required: true
          name: customerId
          in: query
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/StripeCustomerLinkInput'
      responses:
        '200':
          description: The customer is linked to the Stripe customer
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/StripeCustomerLink'
        '400':
          description: Invalid request
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/CustomerError'
        '401':
          description: Missing or invalid Ledger API key
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/CustomerError'
        '404':
          description: >-
            No such customer, or no such Stripe customer in this environment's
            Stripe account
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/CustomerError'
        '409':
          description: >-
            The customer is linked to another Stripe customer
            (stripe_customer_already_linked), another customer has this one
            (stripe_customer_linked_elsewhere), or its metadata names another
            Ledger workspace or environment (stripe_customer_owned_elsewhere)
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/CustomerError'
        '413':
          $ref: '#/components/responses/PayloadTooLarge'
        '429':
          $ref: '#/components/responses/RateLimited'
        '500':
          description: >-
            Unexpected error in Ledger. It has been logged with the request ID;
            retry, or quote request_id when reporting it.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/InternalError'
        '502':
          description: >-
            No Stripe connection for this environment or its key is invalid
            (stripe_not_configured), or Stripe unavailable (stripe_link_failed)
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/CustomerError'
      security:
        - ApiKey: []
      x-codeSamples:
        - lang: python
          label: Python SDK
          source: >-
            import os

            from integrateledger import ledger


            client = ledger(api_key=os.environ["LEDGER_API_KEY"])

            client.link_stripe_customer(customer_id="user_123",
            stripe_customer_id="cus_123")
        - lang: bash
          label: cURL
          source: >-
            curl --request POST
            'https://api.integrateledger.com/v1/customers/stripe-customer?customerId=user_123'
            \
              -H "Authorization: Bearer $LEDGER_API_KEY" \
              -H 'Content-Type: application/json' \
              --data '{"stripeCustomerId":"cus_123"}'
        - lang: typescript
          label: TypeScript SDK
          source: >-
            import ledger from "integrateledger";


            const client = new ledger({ apiKey: process.env.LEDGER_API_KEY ?? ""
            });

            await client.linkStripeCustomer({ customerId: "user_123", body: {
            stripeCustomerId: "cus_123" } });
components:
  schemas:
    StripeCustomerLinkInput:
      type: object
      properties:
        stripeCustomerId:
          type: string
          maxLength: 255
          description: >-
            The customer's existing Stripe customer, in the Stripe account
            connected for this environment.
      required:
        - stripeCustomerId
      additionalProperties: false
    StripeCustomerLink:
      type: object
      properties:
        customerId:
          type: string
        stripeCustomerId:
          type: string
      required:
        - customerId
        - stripeCustomerId
    CustomerError:
      type: object
      properties:
        error:
          type: string
      required:
        - error
    InternalError:
      type: object
      properties:
        error:
          type: string
          example: internal_error
        request_id:
          type: string
          nullable: true
      required:
        - error
        - request_id
    PayloadTooLargeError:
      type: object
      properties:
        error:
          type: string
          example: payload_too_large
      required:
        - error
    RateLimitError:
      type: object
      properties:
        error:
          type: string
          example: rate_limited
      required:
        - error
  responses:
    PayloadTooLarge:
      description: 'The request body is over the limit: 1 MiB, or 5 MiB for a webhook.'
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/PayloadTooLargeError'
    RateLimited:
      description: >-
        Too many requests. Wait Retry-After seconds, then retry. See the rate
        limits guide.
      headers:
        Retry-After:
          description: Seconds until a request is allowed again
          schema:
            type: integer
        RateLimit-Limit:
          description: 'The burst: requests allowed at once'
          schema:
            type: integer
        RateLimit-Remaining:
          description: Requests left right now
          schema:
            type: integer
        RateLimit-Reset:
          description: Seconds until the full burst is back
          schema:
            type: integer
        RateLimit-Policy:
          description: >-
            The sustained rate per second and the burst, such as
            200;w=1;burst=1000
          schema:
            type: string
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/RateLimitError'
  securitySchemes:
    ApiKey:
      type: http
      scheme: bearer

````

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