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

# Cancel an offer

> Cancels an `active` offer and returns it. The buyer's link stops offering checkout and
shows a cancelled offer instead, so a cancelled offer can no longer be paid.

Only an `active` offer can be cancelled: one already ordered, already cancelled, or
lapsed is refused with 422 `invalid_state`. Cancellation is final — there is no
re-activation, and a new offer has to be created.

The optional `cancellation_reason` is stored with the offer and read back as
`cancelled_reason`. It is for your own records; nothing is sent to the buyer.



## OpenAPI

````yaml /api-reference/commerce-v1.openapi.json post /v1/commerce/offers/{id}/cancel
openapi: 3.0.3
info:
  description: >-
    Public REST API for COPE vendor integrations. Authenticate with a COPE API
    key or Clerk bearer token and call the endpoints described below.
  title: COPE Public API
  version: v1
servers:
  - description: Production
    url: https://api.cope.com
security:
  - cope_sk: []
paths:
  /v1/commerce/offers/{id}/cancel:
    post:
      tags:
        - Offers
      summary: Cancel an offer
      description: >-
        Cancels an `active` offer and returns it. The buyer's link stops
        offering checkout and

        shows a cancelled offer instead, so a cancelled offer can no longer be
        paid.


        Only an `active` offer can be cancelled: one already ordered, already
        cancelled, or

        lapsed is refused with 422 `invalid_state`. Cancellation is final —
        there is no

        re-activation, and a new offer has to be created.


        The optional `cancellation_reason` is stored with the offer and read
        back as

        `cancelled_reason`. It is for your own records; nothing is sent to the
        buyer.
      operationId: cancelCommerceOffer
      parameters:
        - description: id public identifier.
          example: of_A1b2C3d4E5f6
          in: path
          name: id
          required: true
          schema:
            example: of_A1b2C3d4E5f6
            pattern: ^of_[A-Za-z0-9]{12}$
            type: string
      requestBody:
        content:
          application/json:
            schema:
              properties:
                cancellation_reason:
                  description: >-
                    Free text kept with the offer and read back as
                    `cancelled_reason`. The buyer is never shown it.
                  maxLength: 500
                  type: string
              type: object
      responses:
        '200':
          content:
            application/json:
              schema:
                additionalProperties: false
                properties:
                  data:
                    additionalProperties: false
                    properties:
                      buyer_profile:
                        additionalProperties: false
                        properties:
                          address:
                            additionalProperties: false
                            properties:
                              city:
                                type: string
                              line1:
                                type: string
                              line2:
                                nullable: true
                                type: string
                              postal_code:
                                type: string
                            required:
                              - city
                              - line1
                              - line2
                              - postal_code
                            type: object
                          company_name:
                            nullable: true
                            type: string
                          country:
                            nullable: true
                            type: string
                          email:
                            type: string
                          first_name:
                            nullable: true
                            type: string
                          last_name:
                            nullable: true
                            type: string
                          locale:
                            type: string
                          phone:
                            description: >-
                              Buyer's phone number, as captured when the offer
                              was created.
                            nullable: true
                            type: string
                          state:
                            nullable: true
                            type: string
                          type:
                            type: string
                          vat_id:
                            nullable: true
                            type: string
                        required:
                          - type
                          - email
                          - state
                          - locale
                          - vat_id
                          - address
                          - country
                          - last_name
                          - first_name
                          - company_name
                        type: object
                      cancelled_at:
                        type: string
                      cancelled_reason:
                        nullable: true
                        type: string
                      created_at:
                        type: string
                      embed_origin:
                        description: >-
                          The one origin allowed to frame `embed_url`. Absent —
                          not null — when the offer cannot be framed.
                        pattern: ^https://[^@/?#\s]+$
                        type: string
                      embed_url:
                        description: >-
                          Pass to the Checkout SDK's `mountCheckout` on
                          `embed_origin` to open this offer's checkout inside
                          your own page. The framed page waits for the SDK's
                          mount handshake, so a bare iframe never loads. The
                          same checkout `public_url` redirects to, not a second
                          one. Absent — not null — when the offer cannot be
                          framed.
                        type: string
                      expires_at:
                        description: >-
                          When the offer link stops working. Null when the offer
                          never expires, which is what an offer created without
                          `link_expiration` gets.
                        nullable: true
                        type: string
                      id:
                        example: of_A1b2C3d4E5f6
                        pattern: ^of_[A-Za-z0-9]{12}$
                        type: string
                      last_buyer_notification_at:
                        nullable: true
                        type: string
                      next_resend_available_at:
                        nullable: true
                        type: string
                      order:
                        additionalProperties: false
                        nullable: true
                        properties:
                          id:
                            example: ord_A1b2C3d4E5f6
                            pattern: ^ord_[A-Za-z0-9]{12}$
                            type: string
                        required:
                          - id
                        type: object
                      ordered_at:
                        nullable: true
                        type: string
                      payment_plan_snapshot:
                        additionalProperties: false
                        properties:
                          currency:
                            type: string
                          custom_plan_name:
                            nullable: true
                            type: string
                          installment_count:
                            nullable: true
                            type: integer
                          interval:
                            nullable: true
                            type: string
                          interval_count:
                            nullable: true
                            type: integer
                          plan_type:
                            type: string
                          price_minor_units:
                            type: integer
                          quantity:
                            type: integer
                          recurring_amount_minor_units:
                            nullable: true
                            type: integer
                          second_payment_in:
                            nullable: true
                            type: integer
                          shipping_price_minor_units:
                            nullable: true
                            type: integer
                          trial_days:
                            nullable: true
                            type: integer
                        required:
                          - plan_type
                          - currency
                          - quantity
                          - price_minor_units
                          - shipping_price_minor_units
                          - recurring_amount_minor_units
                          - interval
                          - interval_count
                          - installment_count
                          - second_payment_in
                          - trial_days
                          - custom_plan_name
                        type: object
                      product:
                        additionalProperties: false
                        nullable: true
                        properties:
                          id:
                            example: prod_A1b2C3d4
                            pattern: ^prod_[A-Za-z0-9]{8}$
                            type: string
                          name:
                            type: string
                        required:
                          - id
                          - name
                        type: object
                      public_url:
                        type: string
                      regeneration_eligible:
                        type: boolean
                      status:
                        type: string
                    required:
                      - buyer_profile
                      - cancelled_at
                      - cancelled_reason
                      - created_at
                      - expires_at
                      - id
                      - last_buyer_notification_at
                      - next_resend_available_at
                      - order
                      - ordered_at
                      - payment_plan_snapshot
                      - product
                      - public_url
                      - regeneration_eligible
                      - status
                    type: object
                required:
                  - data
                type: object
          description: Successful response
        '400':
          content:
            application/problem+json:
              examples:
                invalid_cancellation_reason:
                  summary: Non-string cancellation_reason
                  value:
                    code: invalid_request
                    detail: null
                    errors:
                      - code: invalid
                        detail: cancellation_reason must be a string
                        param: cancellation_reason
                    request_id: req_123
                    status: 400
                    title: Invalid Request
                    type: https://docs.cope.com/errors/invalid_request
                unparsable_request:
                  summary: Unparsable request
                  value:
                    code: invalid_request
                    detail: >-
                      The request could not be parsed. Check the query string
                      and the request body.
                    request_id: req_123
                    status: 400
                    title: Invalid Request
                    type: https://docs.cope.com/errors/invalid_request
              schema:
                $ref: '#/components/schemas/PublicProblemDetail'
          description: Invalid request
        '401':
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/PublicProblemDetail'
          description: Missing or invalid bearer token
        '403':
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/PublicProblemDetail'
          description: Bearer token is not authorized for this route
        '404':
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/PublicProblemDetail'
          description: Resource not found
        '422':
          content:
            application/problem+json:
              examples:
                offer_cancel_invalid_state:
                  summary: Offer cannot be cancelled in its current state
                  value:
                    code: invalid_state
                    detail: cannot cancel offer in ordered state
                    errors:
                      - code: invalid_state
                        detail: cannot cancel offer in ordered state
                    request_id: req_123
                    status: 422
                    title: Invalid State
                    type: https://docs.cope.com/errors/invalid_state
              schema:
                $ref: '#/components/schemas/PublicProblemDetail'
          description: The offer is not in a state this action allows
      security:
        - cope_sk: []
components:
  schemas:
    PublicProblemDetail:
      additionalProperties: false
      properties:
        code:
          type: string
        detail:
          nullable: true
          type: string
        errors:
          items:
            additionalProperties: false
            properties:
              code:
                type: string
              detail:
                type: string
              param:
                type: string
            required:
              - code
              - detail
            type: object
          type: array
        request_id:
          type: string
        status:
          type: integer
        title:
          type: string
        type:
          type: string
      required:
        - type
        - title
        - status
        - code
        - request_id
      type: object
  securitySchemes:
    cope_sk:
      description: >-
        Bearer credential for the public API. Vendor integrations should send a
        live COPE API key (`cope_sk_live_*`). Clerk bearer tokens are also
        accepted when paired with `X-Cope-Business-Id`.
      scheme: bearer
      type: http

````