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

# Add a participant

> Adds one person to a program and returns their participant record, with a link they can use to claim their reward. The key's owner must be an admin of the company, and the account needs an active Hoppier subscription.


> [!WARNING]
>
> **Units.** `amount` on this request is in **dollars** (`25` means \$25.00). A program's `amount` in every response is in **cents** (`2500` means \$25.00).

> [!NOTE]
>
> There is no sandbox. This adds a real person to a real program and uses one of your invitations. Test on a program you made for the purpose, with your own email addresses.

* **Uses one invitation** from your plan, like adding someone in the dashboard.
* **Already in the program?** You get their existing record back, no invitation is used and no email is sent. If your account is set up to allow the same person twice in a program, they're added again.
* **No email unless you ask.** Hoppier emails the person only when `sendEmailAsUsersAreAdded` is `true`. Otherwise, send them `inviteUrl` yourself. A scheduled invite email that your admins set up for the program still goes to everyone who hasn't claimed yet, including people added here.
* **Retries.** Send an `Idempotency-Key` so a retried request never adds anyone twice.


## OpenAPI

````yaml /openapi.yaml post /participants
openapi: 3.1.0
info:
  title: Hoppier API
  version: 1.0.0-beta
  summary: Read your Hoppier programs and add people to them.
  description: >
    The Hoppier API is in **closed beta**. Everything in this reference runs in
    production today.


    - API keys are issued by Hoppier during the beta. Email
    [support@hoppier.com](mailto:support@hoppier.com) to request one.

    - There is no sandbox. Every key works against your live account.

    - We add fields and error codes without notice, so ignore anything you don't
    recognise. We don't rename or remove a documented field without announcing
    it on the [changelog](https://docs.hoppier.com/changelog) first.
  termsOfService: https://www.hoppier.com/terms
  contact:
    name: Hoppier support
    email: support@hoppier.com
servers:
  - url: https://app.hoppier.com/api/v1
    description: Production
security:
  - apiKey: []
tags:
  - name: Account
    description: Check an API key and see which account it belongs to.
  - name: Programs
    description: Read the reward programs in your account.
  - name: Participants
    description: Add people to a program.
paths:
  /participants:
    post:
      tags:
        - Participants
      summary: Add a participant
      description: >
        Adds one person to a program and returns their participant record, with
        a link they can use to claim their reward. The key's owner must be an
        admin of the company, and the account needs an active Hoppier
        subscription.
      operationId: addParticipant
      parameters:
        - $ref: '#/components/parameters/IdempotencyKey'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/AddParticipantRequest'
            examples:
              basic:
                summary: Add someone and share the link yourself
                value:
                  program: prg_Z8q3NnW2xK
                  email: ada@example.com
                  firstName: Ada
                  lastName: Lovelace
              withEmail:
                summary: Add someone and have Hoppier email them
                value:
                  program: prg_Z8q3NnW2xK
                  email: ada@example.com
                  firstName: Ada
                  lastName: Lovelace
                  sendEmailAsUsersAreAdded: true
      responses:
        '200':
          description: >
            The participant: the new record, or the existing one if they were
            already in the program.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Participant'
              example:
                id: pcpt_R4k1LmQ8vB
                object: participant
                email: ada@example.com
                firstName: Ada
                lastName: Lovelace
                program: prg_Z8q3NnW2xK
                status: pending
                inviteUrl: >-
                  https://app.hoppier.com/signup/employee?inviteToken=eyJhbGciOiJIUzI1NiIs...
        '400':
          description: >
            The request was refused. Today this status covers several cases: a
            missing or malformed API key, invalid fields, an `amount` the
            program doesn't accept, an `Idempotency-Key` that isn't a UUID, a
            program that has ended, and an account without an active
            subscription. Read `message`; `parameter` names the field when there
            is one.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
              examples:
                programEnded:
                  summary: The program has ended
                  value:
                    message: Cannot add participants to expired program Team lunch.
                amountNotAllowed:
                  summary: amount sent for a fixed-amount program
                  value:
                    message: >-
                      amount can only be set on a custom-amount program, in an
                      account with custom program amounts enabled
                    parameter: amount
                badIdempotencyKey:
                  summary: Idempotency-Key isn't a UUID
                  value:
                    message: Idempotency-Key must be a UUID
                    parameter: Idempotency-Key
                noSubscription:
                  summary: No active subscription
                  value:
                    message: Company does not have an active subscription.
        '401':
          description: >
            The API key is wrong, unknown or switched off, or its owner isn't an
            admin of the company or of this program.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
              examples:
                invalidKey:
                  summary: Wrong or unknown key
                  value:
                    message: user is unauthorized
                notAdmin:
                  summary: The key's owner isn't an admin
                  value:
                    message: Not admin user
        '402':
          description: >
            Your plan has no sends left for this plan year. This response
            carries its code in `code`; it moves to `error` in the next API
            update.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
              example:
                message: >-
                  You've used all 100 send credits for this plan year. They
                  renew on 6 Nov 2027. To send more now, contact us to move to a
                  bigger plan.
                code: send_credits_exhausted
        '404':
          $ref: '#/components/responses/ProgramNotFound'
        '409':
          description: >
            `request_outcome_unknown`: an earlier request with this
            `Idempotency-Key` started more than 5 minutes ago and never
            finished. Check whether the person was added before you retry with a
            new key.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
              example:
                error: request_outcome_unknown
                message: >-
                  This request started a while ago and its outcome was never
                  recorded. Check whether the participant was added before
                  sending it again with a new Idempotency-Key.
        '422':
          description: >
            `idempotency_key_reuse`: this `Idempotency-Key` was already used
            with a different request body.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
              example:
                error: idempotency_key_reuse
                message: >-
                  Idempotency-Key was already used with a different request
                  body.
        '429':
          $ref: '#/components/responses/RateLimited'
        '500':
          $ref: '#/components/responses/ServerError'
        '503':
          description: >
            Retry after the number of seconds in `Retry-After`.


            - `in_progress`: a request with the same `Idempotency-Key` is still
            running.

            - `rate_limit_unavailable`: Hoppier couldn't check your rate limit.
            Nothing was added.
          headers:
            Retry-After:
              $ref: '#/components/headers/RetryAfter'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
              examples:
                inProgress:
                  summary: Same Idempotency-Key, still running
                  value:
                    error: in_progress
                    message: An identical request is being processed. Retry shortly.
                rateLimitUnavailable:
                  summary: Rate limit couldn't be checked
                  value:
                    error: rate_limit_unavailable
                    message: Please retry shortly.
      x-codeSamples:
        - lang: bash
          label: curl
          source: |
            curl -X POST https://app.hoppier.com/api/v1/participants \
              -H "Authorization: Bearer $HOPPIER_API_KEY" \
              -H "Content-Type: application/json" \
              -H "Idempotency-Key: $(uuidgen | tr 'A-Z' 'a-z')" \
              -d '{
                "program": "prg_Z8q3NnW2xK",
                "email": "ada@example.com",
                "firstName": "Ada",
                "lastName": "Lovelace"
              }'
        - lang: javascript
          label: Node.js
          source: >
            import { randomUUID } from 'node:crypto';


            const res = await
            fetch('https://app.hoppier.com/api/v1/participants', {
              method: 'POST',
              headers: {
                Authorization: `Bearer ${process.env.HOPPIER_API_KEY}`,
                'Content-Type': 'application/json',
                // Reuse the same key if you retry this add.
                'Idempotency-Key': randomUUID(),
              },
              body: JSON.stringify({
                program: 'prg_Z8q3NnW2xK',
                email: 'ada@example.com',
                firstName: 'Ada',
                lastName: 'Lovelace',
              }),
            });

            const participant = await res.json();
        - lang: python
          label: Python
          source: |
            import os
            import uuid

            import requests

            res = requests.post(
                "https://app.hoppier.com/api/v1/participants",
                headers={
                    "Authorization": f"Bearer {os.environ['HOPPIER_API_KEY']}",
                    # Reuse the same key if you retry this add.
                    "Idempotency-Key": str(uuid.uuid4()),
                },
                json={
                    "program": "prg_Z8q3NnW2xK",
                    "email": "ada@example.com",
                    "firstName": "Ada",
                    "lastName": "Lovelace",
                },
                timeout=30,
            )
            participant = res.json()
components:
  parameters:
    IdempotencyKey:
      name: Idempotency-Key
      in: header
      required: false
      description: >
        A UUID you generate for this add. If the request fails or times out,
        send it again with the **same** key and body: Hoppier won't add the
        person twice.


        - Same key, same body, already finished: you get the original response
        again.

        - Same key, different body: `422 idempotency_key_reuse`.

        - Same key while the first request is still running: `503 in_progress`;
        retry after `Retry-After`.

        - The first request started more than 5 minutes ago and never finished:
        `409 request_outcome_unknown`.


        Keys are remembered for at least a day. Use a new key for each new add.
      schema:
        type: string
        format: uuid
      example: 4f9d3b1e-8c2a-4e1f-9a7b-2d6c5e8f1a3b
  schemas:
    AddParticipantRequest:
      type: object
      required:
        - program
        - email
      properties:
        program:
          type: string
          description: Id of the program to add the person to. Starts with `prg_`.
          examples:
            - prg_Z8q3NnW2xK
        email:
          type: string
          format: email
          description: The person's email address. Hoppier stores it in lowercase.
          examples:
            - ada@example.com
        firstName:
          type: string
          description: The person's first name.
          examples:
            - Ada
        lastName:
          type: string
          description: >-
            The person's last name. First and last name together can be at most
            24 characters.
          examples:
            - Lovelace
        amount:
          type: number
          description: >
            This person's amount, in **dollars** (`25` means \$25.00), at most
            `500`.


            Only for programs where each person has their own amount, in
            accounts with custom program amounts turned on. Leave it out for a
            fixed-amount program: the program's amount applies, and sending one
            is refused.
          examples:
            - 25
        sendEmailAsUsersAreAdded:
          type: boolean
          description: >
            `true` emails the person their invite now, using the program's "as
            users are added" invite email (if the program has none, nothing is
            sent). Leave it out to add them without an email. Send a JSON
            boolean: the string `"false"` counts as `true`.
          examples:
            - false
    Participant:
      type: object
      description: A person in a program.
      required:
        - id
        - object
        - email
        - program
        - status
      properties:
        id:
          type: string
          description: The participant's id. Starts with `pcpt_`.
          examples:
            - pcpt_R4k1LmQ8vB
        object:
          type: string
          const: participant
        email:
          type: string
          format: email
          examples:
            - ada@example.com
        firstName:
          type: string
          description: First word of the person's name. Left out when no name was given.
          examples:
            - Ada
        lastName:
          type: string
          description: Second word of the person's name. Left out when there isn't one.
          examples:
            - Lovelace
        program:
          type: string
          description: Id of the program. Starts with `prg_`.
          examples:
            - prg_Z8q3NnW2xK
        status:
          type: string
          enum:
            - pending
            - active
            - inactive
          description: >
            `pending`: invited, hasn't claimed yet. `active`: claimed.
            `inactive`: no longer active in the program.
        inviteUrl:
          type: string
          format: uri
          description: >
            The link the person opens to claim their reward: Hoppier's sign-up
            page, or the sign-in page if they already have an account. Valid for
            30 days. Anyone who has it can use it, so send it only to that
            person.
        publicInviteUrl:
          type: string
          format: uri
          description: >
            Only for accounts with public program links turned on: the program's
            public join link. It's the same for everyone in the program, and
            anyone who has it can join. Valid for 90 days.
    Error:
      type: object
      required:
        - message
      properties:
        message:
          type: string
          description: What went wrong, in plain words. Don't parse it; it can change.
        error:
          type: string
          description: >
            A machine-readable code. Present on some errors today
            (`rate_limited`, `rate_limit_unavailable`, `in_progress`,
            `idempotency_key_reuse`, `request_outcome_unknown`), and on every
            error after the next API update.
        parameter:
          type: string
          description: >-
            On some validation errors, the field (or the value) that was
            refused.
        code:
          type: string
          description: >-
            Only on the 402 `send_credits_exhausted` response today. It moves to
            `error` in the next API update.
  responses:
    ProgramNotFound:
      description: >-
        No program with this id in the key's company. A program in another
        company gets the same answer.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
          example:
            message: program prg_Z8q3NnW2xK not found
    RateLimited:
      description: >
        Too many requests. Wait the number of seconds in `Retry-After`, then
        retry. The per-account limit answers in JSON; the per-address limit
        answers in plain text today and can send a fraction of a second, so
        round it up. See [Errors and rate
        limits](/guides/errors-and-rate-limits).
      headers:
        Retry-After:
          $ref: '#/components/headers/RetryAfter'
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
          example:
            error: rate_limited
            message: >-
              Too many requests for this account. Retry after the number of
              seconds in Retry-After.
        text/plain:
          schema:
            type: string
          example: Too Many Requests
    ServerError:
      description: >
        Something went wrong on Hoppier's side. Retry with backoff; if it keeps
        happening, email support@hoppier.com. Today `message` can contain
        internal text: log it, but don't show it to your users.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
          example:
            message: An unexpected error occurred.
  headers:
    RetryAfter:
      description: Seconds to wait before retrying.
      schema:
        type: string
      example: '30'
  securitySchemes:
    apiKey:
      type: http
      scheme: bearer
      bearerFormat: hk_live.<prefix>.<secret>
      description: >
        Send your key in the `Authorization` header: `Authorization: Bearer
        hk_live.…`. The word `Bearer` is case-sensitive.


        Keys are issued by Hoppier during the beta and shown once. Treat them
        like passwords and never put one in code that runs in a browser or an
        app.

````

This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.