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

# Integrating invite codes

> Give each profile a personal invite code, check it when a new customer signs up on your site, and attribute the new profile to the referrer with Omneo Invites.

By the end of this guide your website shows each signed-in profile (the **referrer**) a personal **invite code**, accepts that code when a new customer signs up, and tells Omneo Invites to record the referral on the new profile. Omneo issues the codes, checks them, and writes the referral. Your backend shows the code, collects it at sign-up, and calls Invites once the new profile exists in Omneo.

Invite codes are the code-based counterpart of email invites. For what they are and how they differ, read [Invite codes](/experiences/invites/overview#invite-codes). For the email flow, read [Integrating Omneo Invites](/dev-guides/frontend/invites-plugin). Every endpoint is documented in the [Invites API reference](/dev-guides/frontend/invites-api#invite-codes).

## What you build and what Omneo provides

| Item | Who provides it | Notes |
| - | - | - |
| Invite codes enabled for your tenant | Omneo | Until they are enabled, every invite code endpoint returns `404` with `Invite codes are not enabled.` |
| Invites API base URL `https://api.[tenant].getomneo.com/invites/` | Omneo | The same service as email invites. |
| Machine token for your backend | Omneo | A long-lived server credential for checking and accepting codes. Ask for one token per calling service, so each caller can be identified and revoked on its own. |
| Omneo API token | Omneo, or created in CX Manager if you have access | Long-lived and server-side only. Used to look up the new profile in Omneo and, if you show codes with an ID token, to mint ID tokens. See [API tokens](/dev-guides/core-setup/api-tokens). |
| Identity handle for your platform's customer IDs | Omneo | For example `shopify-[store-handle]`. Lets you find a profile from the customer ID you already hold. |
| Share link, sign-up field, and on-site messaging | **You** | |
| Staging equivalents of all of the above | Omneo | |

## Flow

```mermaid theme={null}
sequenceDiagram
    autonumber
    participant R as Referrer (signed in)
    participant S as Your backend
    participant INV as Omneo Invites
    participant F as New customer
    participant O as Omneo API

    R->>S: Open the refer page
    S->>INV: POST /api/v1/profiles/{id}/invite-code
    INV-->>S: 200 or 201, code
    S-->>R: Show the code and a share link
    R-->>F: Shares the code
    F->>S: Sign-up form with the code
    S->>INV: GET /api/v1/invite-codes/{code}
    INV-->>S: valid true or false
    S->>S: Create the customer account
    Note over S,O: Your platform syncs the new customer to Omneo
    S->>O: POST /v3/profiles/search-id
    O-->>S: The new profile's id
    S->>INV: POST /api/v1/invite-codes/{code}/accept
    INV-->>S: 200, referral recorded
```

## Show the referrer's code

A profile has no code until one is requested. The first request issues one, and every later request returns the same code. A code never changes once issued, so you can store it against your customer record after the first call and skip later requests.

<Steps>
  <Step title="Identify the referrer server-side">
    Establish which profile is signed in on your platform. Never trust a customer ID sent from the browser. On a Shopify App Proxy, verify the `signature` query parameter before reading `logged_in_customer_id`, as described in [Integrating Omneo Invites](/dev-guides/frontend/invites-plugin#the-refer-step).
  </Step>

  <Step title="Get or issue the code">
    With the machine token and the referrer's Omneo profile ID:

    ```shell theme={null}
    curl -X POST https://api.[tenant].getomneo.com/invites/api/v1/profiles/{profileId}/invite-code \
      -H "Authorization: Bearer ${MACHINE_TOKEN}"
    ```

    Or, if you already mint an [Omneo ID token](/dev-guides/frontend/omneo-id) for the referrer, call the profile's own route instead. It needs no profile ID:

    ```shell theme={null}
    curl -X POST https://api.[tenant].getomneo.com/invites/api/v1/me/invite-code \
      -H "Authorization: Bearer ${OMNEO_ID_TOKEN}"
    ```

    Both return the same body. `201` means the code was issued by this request, and `200` means the profile already had one:

    ```json theme={null}
    {
      "data": {
        "code": "K7MQ4XRT",
        "profile_id": "01H8Z..."
      }
    }
    ```

    A `503` with `A invite code could not be issued. Please try again.` is safe to retry once.
  </Step>

  <Step title="Display and share it">
    Codes are eight characters from `ABCDEFGHJKMNPQRSTUVWXYZ23456789`. They leave out `0`, `O`, `1`, `I`, and `L`, so a code read aloud or copied from a screen is unambiguous. You can display it in groups, such as `K7MQ 4XRT`, because Invites ignores spaces and hyphens when it reads a code.

    Build the share link to point at your own sign-up page, for example `https://[your-site]/signup?invite_code=K7MQ4XRT`, and pre-fill the sign-up field from it.
  </Step>
</Steps>

## Check the code at sign-up

Check the code while the customer is filling in the form, so they can correct a typo before the account is created.

```shell theme={null}
curl https://api.[tenant].getomneo.com/invites/api/v1/invite-codes/K7MQ4XRT \
  -H "Authorization: Bearer ${MACHINE_TOKEN}"
```

```json theme={null}
{ "data": { "code": "K7MQ4XRT", "valid": true } }
```

An unknown code returns `404` with `"valid": false`. The response never says who owns a code, and your site should not either. Send whatever the customer typed: Invites ignores case, spaces, and hyphens, and returns the normalised `code`.

Run this check from your backend, because it needs the machine token. Debounce it as the customer types. The Invites API allows 60 requests per minute per token, and the limit is shared by every customer using your site.

A valid code at sign-up does not guarantee the referral. The accept step makes the final decision, for example when the new customer turns out to have a referrer already.

## Attribute the new profile

Call accept after the customer account exists and has reached Omneo. Invites reads the profile from Omneo, so it must be there first.

<Steps>
  <Step title="Keep the code through sign-up">
    Store the normalised code with the new customer account, or in the session, so it is available after the account is created. If your platform syncs to Omneo asynchronously, a background job that runs after the sync is the most reliable place to call accept.
  </Step>

  <Step title="Find the new profile in Omneo">
    Look the profile up by your platform's customer ID with your Omneo API token. See [Searching by ID or identity](/api-reference/profile/get-profile-by-id-or-identity).

    ```shell theme={null}
    curl -X POST https://api.[tenant].getomneo.com/api/v3/profiles/search-id \
      -H "Authorization: Bearer ${OMNEO_TOKEN}" \
      -H "Content-Type: application/json" \
      -d '{ "type": "shopify-[store-handle]", "id": "7290416496913" }'
    ```

    The response is the profile, and `data.id` is the ID to send to Invites. A `404` means the customer has not synced to Omneo yet. Retry later with backoff.
  </Step>

  <Step title="Accept the code">
    ```shell theme={null}
    curl -X POST https://api.[tenant].getomneo.com/invites/api/v1/invite-codes/K7MQ4XRT/accept \
      -H "Authorization: Bearer ${MACHINE_TOKEN}" \
      -H "Content-Type: application/json" \
      -d '{ "profile_id": "01H9A...", "source": "[your-site]" }'
    ```

    | Field | Rule |
    | - | - |
    | `profile_id` | Required. The new profile's Omneo ID. |
    | `source` | Optional, up to 191 characters. Names the caller, such as your site or app, so Omneo can report which channel each referral came from. |

    ```json theme={null}
    { "profile_id": "01H9A...", "statuses": ["member", "referred"] }
    ```

    `statuses` is the profile's full status list after the accept.
  </Step>

  <Step title="Handle the response">
    | Status | Body | What to do |
    | - | - | - |
    | `200` | `{"profile_id": "...", "statuses": [...]}` | Done. The referral is recorded. |
    | `404` | `{"data":{"error":"The profile could not be found."}}` | The profile has not reached Omneo yet. Retry with backoff. |
    | `404` | `{"data":{"error":"The invite code is not valid."}}` | No profile owns this code. Do not retry. |
    | `409` | `{"data":{"error":"The profile has already been attributed to a referrer."}}` | The profile already has a referrer. Do not retry. |
    | `422` | `{"data":{"error":"A profile cannot use its own invite code."}}` | The customer entered their own code. Do not retry. |
    | `422` | `{"message":"The given data was invalid.","errors":{"profile_id":["..."]}}` | Fix the request. |
    | `503` | `{"data":{"error":"A invite code could not be issued. Please try again."}}` | Another request for the same profile was in progress. Retry once. |
    | `404` | `{"data":{"error":"Invite codes are not enabled."}}` | Invite codes are off for this tenant. Contact Omneo. |
    | `429` | Throttled | Back off. |

    The customer's account already exists by this point, so a failed accept costs only the referral credit. Log the code, your customer ID, the profile ID, and the response so Omneo support can trace a missing referral.
  </Step>
</Steps>

## One referrer per profile

The first referrer wins, whether the referral came from a code or an email invite. Invites refuses a code with `409` when the profile already has `invites.invited_by`, has redeemed a code before, or has accepted an email invite. Accepting a code declines any email invites still pending for the new profile's email address.

If a new customer arrives with both an email invite link and a code, accept the email invite first. It is the more specific referral, and the code then answers `409`.

## What Omneo records

A successful accept updates the new profile with:

| Field | Value |
| - | - |
| `invites.invited_by` custom attribute | The referrer's profile ID, the same attribute an accepted email invite writes. |
| `invites.invite_code` custom attribute | The code that was used. |
| Statuses | The default statuses of the Invite Definition configured for invite codes, added to the profile's existing statuses. |

Because the referral uses `invites.invited_by`, the referrer's `total_referred` aggregation counts it, and referral [Reactions](/concepts/automation/reactions) treat it like any other referral. See [Referral incentives](/experiences/invites/overview#referral-incentives).

<Note>
  The referral is written to a profile that already exists, so Omneo raises `profile.updated`, not `profile.created`. A Trigger that only checks `invites.invited_by` on profile creation will miss invite code referrals. Agree the Reactions with Omneo before go-live.
</Note>

## Things not to do

* **Do not write `invites.invited_by` or `invites.invite_code` yourself.** Call accept, so the one-referrer rule, the default statuses, and the referral record all apply.
* **Do not put the machine token or the Omneo API token in browser JavaScript.** Check and accept codes from your backend.
* **Do not show who owns a code.** The check endpoint does not reveal it, and your sign-up page should not infer it.

## Security checklist

* [ ] The machine token and the Omneo API token live only in server configuration or a secret manager.
* [ ] The referrer's profile is established server-side before a code is fetched for it. Never accept a profile ID or customer ID from the request body.
* [ ] The sign-up code check is rate-limited and debounced on your side. The API limit is a backstop.
* [ ] Every accept is logged with the code, your customer ID, the Omneo profile ID, and the response.

## Go-live checklist

Before you start, Omneo confirms that invite codes are enabled for your staging tenant, issues your machine token, and agrees the Reactions and statuses for code referrals with you.

1. Sign in as a test referrer and open your refer page. Expect `201` the first time and `200` with the same code after that.
2. Check the code with spaces and lower case, for example `k7mq 4xrt`. Expect `200` and `"valid": true`.
3. Check a made-up code. Expect `404` and `"valid": false`.
4. Sign up a new test customer with the code. Once the profile is in Omneo, accept. Expect `200`.
5. Omneo confirms the new profile carries `invites.invited_by` equal to the referrer's profile ID and `invites.invite_code`, and that the referral Reaction fired.
6. Negative checks: accept the same profile again and expect `409`. Accept the referrer's own code for the referrer's profile and expect `422`.

| Concern | Owner |
| - | - |
| Refer page, share link, sign-up field, code check, accept call, retries, logging | You |
| Enabling invite codes, machine token, Invite Definition statuses, Reactions | Omneo |


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