---
title: "Create a payment request"
method: POST
path: "/payments"
tags: ["Payments"]
---

# Create a payment request

`POST /payments`

## Request

- Base URL: `https://osis.co/api/v1/comms`
- URL: `https://osis.co/api/v1/comms/payments`
- Auth: HTTP bearer

Needs comms_send. Select existing public catalog items or a public Whop checkout_url. Supports built-in managed Whop, connected Whop and Shopify. Price and currency come from the provider. All REST callers, including organization API keys used by self-hosted agents, use agent restrictions. Custom amounts, currency overrides, hidden/private plans and arbitrary-amount Stripe checkout are rejected. The workspace must accept the merchant terms first.

## Headers

- `Idempotency-Key` string

## Request body

- object
  - `provider` 'whop' | 'shopify': Connected payment provider. The same endpoint and response serve every provider.
  - `items` object[]: Public Shopify variant IDs, or one public Whop one-time product plan with quantity 1. The provider supplies price and currency.
    - `id` string, required
    - `quantity` integer
  - `description` string
  - `conversation_id` string
  - `send` boolean: Set to true, along with conversation_id, contact_id, or to, and Comms texts the checkout for you using delivery_mode. Check sent and send_error in the response. A sent link doesn't mean it was received or paid.
  - `delivery_mode` 'link' | 'imessage_app': How the checkout is sent. Defaults to imessage_app if your workspace has checkout cards turned on, and link otherwise. imessage_app needs checkout cards turned on, Shopify physical products or permitted Whop purchases priced in USD, a dedicated iMessage line that supports it, and the Comms Messages extension. It never falls back to a link or SMS.
  - `idempotency_key` string: Required for Shopify, supported by Whop, rejected by Stripe. May instead be sent as Idempotency-Key header. Both values must match when supplied together.
  - `checkout_url` string, uri: Existing canonical public Whop checkout URL instead of items. A fresh session tracks the existing public product plan.
  - `discount_codes` string[]: Existing Shopify discount codes. Every code must apply to the cart. Other providers do not support this field.
  - `creator_name` string
  - `contact_id` string
  - `to` string
  - `agent_id` string: The agent this payment is for.
  - `metadata` object
  - `rail` 'whop' | 'shopify': Legacy alias for provider. If both are supplied, they must match.
  - `shopify` object
    - `lines` object[], required
      - `variant_id` string, required
      - `quantity` integer, required
    - `discount_codes` string[]: Discount codes that already exist in your store. Case doesn't matter. Every code has to apply to the cart right away, or you get 422 shopify_discount_not_applicable and nothing is sent. Shopify decides which codes apply and how they combine.
  - `whop` union: Existing public one-time product plan. The plan and product must both be visible and owned by the workspace. Hidden, archived, quick-link and invoice plans are forbidden.
    - object
      - `plan_id` string, required
      - `checkout_url` string, uri: Canonical https://whop.com/checkout/plan_... URL. A fresh session is created for this request.
    - object
      - `plan_id` string
      - `checkout_url` string, uri, required: Canonical https://whop.com/checkout/plan_... URL. A fresh session is created for this request.

## Response `201`

Payment request created, or an existing Shopify/Whop request returned for the same idempotency key.

- CreatePaymentRequestResponse
  - `payment_request` PaymentRequest, required
    - `id` string, required
    - `agent_id` string, nullable
    - `conversation_id` string, nullable
    - `contact_id` string, nullable
    - `to` string, nullable
    - `amount_cents` integer, required: Amount in cents (or the currency's smallest unit). For Shopify, this is an estimate until the paid order is confirmed.
    - `currency` string, required
    - `amount_formatted` string
    - `description` string
    - `creator_name` string
    - `status` 'pending' | 'paid' | 'canceled' | 'partially_refunded' | 'refunded', required: pending: waiting for payment. paid: the provider confirmed it. canceled: you canceled a Whop or Stripe request, or the order was canceled in Shopify. partially_refunded and refunded: part or all of a Shopify payment was refunded.
    - `refunded_cents` integer, required: Total refunded so far, in the payment's currency. 0 if nothing was refunded. Only Shopify reports refunds for now.
    - `refunded_at` string, date-time, nullable: When the latest refund came in.
    - `canceled_at` string, date-time, nullable: When the order was canceled in Shopify. Null for requests canceled in Comms.
    - `settlement` 'org' | 'platform'
    - `url` string, uri, required
    - `checkout_url` string, uri, nullable
    - `rail` 'whop' | 'stripe' | 'shopify', required
    - `whop_plan_id` string, nullable
    - `whop_checkout_config_id` string, nullable
    - `whop_payment_id` string, nullable
    - `stripe_checkout_session_id` string, nullable
    - `metadata` object
    - `paid_at` string, date-time, nullable
    - `created_at` string, date-time, nullable
    - `updated_at` string, date-time, nullable
    - `provider` 'whop' | 'shopify' | 'stripe', required
  - `url` string, uri, required
  - `sent` boolean, required: The text was sent. This doesn't mean it was delivered or paid.
  - `send_error` string

## Other responses

- `400`: Bad request, or a Shopify request is missing its idempotency key
- `401`: API key is missing or wrong.
- `403`: payments_api_disabled: accept the merchant terms to turn on the Payments API. merchant_terms_update_required: accept the new terms version. payments_suspended: Comms suspended payments for your workspace. You also get 403 if your key is missing a scope, or with payment_admin_required. shopify_product_ineligible: a variant can't be sold through Comms. shopify_product_not_approved: Comms hasn't reviewed the variant (workspaces set up before open signup only).
- `409`: The key was reused with different details, your store isn't connected, shopify_install_required: install the Comms app (stores connected with pasted credentials), or shopify_reauthorization_required: connect your store again.
- `422`: Price/currency override, unsupported agent provider, missing catalog source, or unsupported product/currency.

---

[API](https://skmtc.dev/osis/apis/comms-api.md) · [All operations](https://skmtc.dev/osis/apis/comms-api/llms.txt) · [OpenAPI document](https://skmtc.dev/osis/apis/comms-api/revisions/59a4c0ba8936?raw)
