---
title: "Create a product"
method: POST
path: "/api/v1/products"
tags: ["Products"]
---

# Create a product

`POST /api/v1/products`

## Request

- Base URL: `https://peerpush.com`
- URL: `https://peerpush.com/api/v1/products`
- Auth: one of:
  - API key in header `X-PeerPush-Api-Key`
  - API key in query parameter `api_key`

Create a product using a normal API key. Products start private and unclaimed. Open the returned claim link to sign in and claim the product, then confirm its details in the normal editor and choose a plan. Claiming requires account authentication and does not publish the product or make a purchase. Keep the link private. Returns 201 Created with a Location header pointing to /api/v1/products/{id}/status.

## Headers

- `Idempotency-Key` string

## Request body

- CreateProduct
  - `name` string, required
  - `tagline` string, required
  - `description` string, required
  - `websiteUrl` string, uri, required
  - `appStoreUrl` string, nullable
  - `playStoreUrl` string, nullable
  - `mcpServerUrl` string, nullable
  - `cliInstallCommand` string, nullable
  - `apiDocsUrl` string, nullable
  - `mrr` number, nullable
  - `twitterUsername` string, nullable
  - `linkedinUrl` string, nullable
  - `categories` string[]
  - `useCases` string[]
  - `audiences` string[]
  - `pricingType` 'Free' | 'Freemium' | 'Subscription' | 'OneTime' | 'Paid', nullable
  - `startingPrice` number, nullable
  - `platforms` string[]
  - `discountCodes` object[]
    - `id` string
    - `title` string, required
    - `code` string, required
    - `percentOff` integer, required
    - `validUntil` string, date-time, required
  - `logoUrl` string, uri, required
  - `screenshotUrls` string[]
  - `alternatives` object[]
    - `type` 'internal', required
    - `productId` string, required
  - `externalId` string
  - `contactEmail` string, email, required: Contact address for the person who should claim this product. Required. Never published or returned by the API, and only PeerPush staff can see it. PeerPush emails it once, to remind the owner to claim the product before the link expires.

## Response `201`

Create a product

- object
  - `data` CreatedProduct, required
    - `id` string, required: Product ID. Use GET /api/v1/products/{id}/status to retrieve creation status with the originating API key.
    - `status` 'ready' | 'claimed' | 'expired' | 'revoked', required
    - `claimUrl` string, uri, required: Private link to review, edit and claim the product. Treat it as a credential.
    - `expiresAt` string, date-time, required
    - `claimedAt` string, date-time, nullable, required

## Other responses

- `400`: Invalid query parameters (code: VALIDATION_ERROR)
- `401`: Missing, unknown, or revoked API key (code: UNAUTHORIZED or INVALID_API_KEY)
- `403`: API access forbidden
- `404`: Resource not found (code: NOT_FOUND)
- `409`: Idempotency conflict or request size error
- `413`: Idempotency conflict or request size error
- `429`: Hourly per-key quota exhausted (code: RATE_LIMITED)
- `500`: Unexpected server error (code: INTERNAL_ERROR)

---

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