> For the complete documentation index, see [llms.txt](https://developers.tix.xyz/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://developers.tix.xyz/integration-guides/list-a-ticket.md).

# List a ticket for resale

Selling is a three-step handshake. Your backend prepares and signs the listing as the integrator, the ticket owner co-signs it through KYD Labs in your app, and your backend submits it.

You'll need **Create listings** permission on your API key, a registered integrator with a funded treasury, and a KYD OAuth client for your app. [Set up KYD Labs](/getting-started/kyd-setup.md) covers the client, scopes, test fans, and environment pairing.

## 1. Find the ticket

The seller signs in to your app with KYD Labs. Their tickets come from the KYD Fan API's tickets endpoint. A ticket that can be listed on TDP carries `on_chain_metadata.tix_event_id` and `on_chain_metadata.tix_permit_id`; those are its TDP `eventId` and `ticketId`.

## 2. Prepare and sign the listing

On your server, gather the accounts the listing refers to, then sign the listing instruction with your integrator wallet:

```typescript
const ticket = { eventId, ticketId };
const [permit, protocol, identity, feePayer] = await Promise.all([
  client.solana.getPermit(ticket),
  client.solana.getProtocolConfig(),
  client.wallets.getIntegratorAddress({ subject: "PRIMARY_ADDRESS" }),
  client.wallets.getFeePayerAddress(),
]);
const { integrator } = await client.solana.getIntegrator({ identity: identity.address });

const signed = await client.transactions.signIntegrator({
  subject: "PRIMARY_ADDRESS",
  idempotencyKey: `listing:${listingRequestId}`,
  body: {
    tixInstructions: {
      instruction: "list_ticket_v2",
      params: [
        {
          event_id: eventId,
          ticket_id: ticketId,
          ask_price_minor: price, // USDC minor units
          expires_at: expiresAt, // Unix seconds, in the future
          seller: permit.owner,
          storage_fee_payer: feePayer.address,
          listing_integrator_identity: identity.address,
          listing_integrator_authority: integrator.authority,
          usdc_mint: protocol.usdc_mint,
          payout_ata: integrator.treasury_usdc,
          ticket_type_config: permit.ticket_type_config,
        },
      ],
    },
  },
});
// signed.signedTransaction is a base64 transaction signed by your integrator wallet.
```

* `payout_ata` is where the proceeds land when the ticket sells. Paying into your own treasury and settling with the seller yourself is the simplest model.
* Listing spends from your treasury for fees and storage.
* Keep the signed transaction with the seller's session so an interrupted flow can resume without signing again.

## 3. The seller co-signs through KYD

In the browser, send the transaction to the KYD Fan API with the seller's access token:

```http
POST https://api.kydlabs.com/fans/authorizations
Authorization: Bearer <fan access token>
Content-Type: application/json

{ "type": "list", "transactions": ["<signed transaction>"] }
```

KYD accepts the transaction as base64 or hex and returns it hex-encoded, now also signed by the ticket owner:

```json
{ "transactions": ["<hex transaction>"] }
```

## 4. Submit

Back on your server, convert to base64 and submit:

```typescript
const result = await client.transactions.submit({
  body: {
    signedTransaction: Buffer.from(hexTransaction, "hex").toString("base64"),
  },
});
```

The response carries `status`, `transactionSignature`, and `failureReason` when there is one. The status values are the same as for purchases; see [Track the outcome](/integration-guides/secondary-resale.md#track-the-outcome). Only `confirmed` means the ticket is listed. Submitting the same signed transaction again is safe: it can land at most once. After confirmation the listing appears in active listings once it has been indexed.

Cancelling a listing is not documented yet.
