---
title: "Setup Team Payment Method"
method: POST
path: "/billing/team/{team_id}/setup-payment-method"
tags: ["billing"]
---

# Setup Team Payment Method

`POST /billing/team/{team_id}/setup-payment-method`

Attach a payment method to a team's Stripe customer.

Requires: MANAGE_BILLING permission (owner, admin, or billing role)

This endpoint:
1. Creates a Stripe customer for the team if one doesn't exist
2. Attaches the payment method to the team's customer
3. Sets it as the default payment method

Rate-limited to 5 attempts per user per hour. Each attempt captures a
$1 PaymentIntent (real settled charge) for Stripe Radar fraud scoring,
so unbounded retries from a single owner/admin let a bot incinerate the
Stripe fee budget and burn Radar reputation. The cap leaves room for
legitimate retries on declined cards or SCA hiccups while denying bots
unlimited attempts on the same compromised owner account.

Args:
    request: FastAPI request (required by SlowAPI rate limiter).
    team_id: Team UUID.
    body: Payment method ID from Stripe.js.
    auth: Authenticated user context.

Returns:
    Success status and customer ID.

## Path parameters

- `team_id` string, required

## Request body

- SetupTeamPaymentMethodRequest — Request model for setting up a team payment method. See ``SetupPaymentMethodRequest`` for the two-call SCA protocol.
  - `payment_method_id` string, required — Stripe payment method ID from Stripe.js
  - `payment_intent_id` string, nullable — Set only on the post-3DS retry to finalize the existing verification PaymentIntent. Must match the ``payment_intent_id`` returned in the previous ``requires_action`` response.

## Response `200`

Successful Response

- SetupPaymentMethodResponse — Response model for payment method setup. When the issuer requires SCA / 3DS on the verification PaymentIntent the response sets ``requires_action=True`` and ships ``client_secret`` so the frontend can complete the 3DS challenge via ``stripe.handleNextAction`` and re-invoke the same endpoint. The backend re-creates the PaymentIntent with the same idempotency key, Stripe returns the same (now-confirmed) intent, and we finalize.
  - `success` boolean, required
  - `message` string, required
  - `customer_id` string, nullable
  - `requires_action` boolean
  - `client_secret` string, nullable
  - `payment_intent_id` string, nullable

## Other responses

- `422` — Validation Error

---

[API](https://skmtc.net/pioneer/apis/brain-api.md) · [All operations](https://skmtc.net/pioneer/apis/brain-api/llms.txt) · [OpenAPI document](https://skmtc-service-staging.skmtc.workers.dev/v1/apis/pioneer/brain-api/versions/31dfe831e079/schema)
