Which API endpoints are RevenueDot extensions?

These endpoints exist only in RevenueDot. They use the same auth, errors and list envelope as REST API v2. The dashboard is built on them, so everything the dashboard does, a script or an AI agent can do too.

Base URL: your server, for example http://localhost:8787 or https://revenuedot.example.com. The examples read REVENUEDOT_URL, PUBLIC_KEY, SECRET_KEY and PROJECT_ID from your shell.

Operations on this page (47)#

Dashboard auth#

Sign-up, sign-in, password reset, email confirmation, invites and account settings for the dashboard. The session cookie also authorizes REST API v2.

Whether sign-up is open#

GET /auth/config · Auth: none · RevenueDot extension

What the sign-in page needs before it shows a form.

Example request

Shell
curl -s "$REVENUEDOT_URL/auth/config"

Responses

  • 200: The config.

Example 200 response:

JSON
{
  "edition": "self-hosted",
  "signup": "closed"
}

Create a dashboard account#

POST /auth/signup · Auth: none · RevenueDot extension

Creates the user and a first project, and sets the rd_session cookie (30 days; Secure over https). On a self-hosted server only the first account (the owner) can sign up, unless the server runs with REVENUEDOT_ALLOW_SIGNUP=true.

With invite_token (from an invite link), the account joins the inviting project instead of getting a new one, and sign-up works even where it is closed. The email must be the invited address; the account counts as verified. On RevenueDot Cloud, an account without an invite gets an email with a confirmation link (valid 24 hours).

Request body (application/json)

Field Type Required Description
email string yes
password string yes
name string no
project_name string no Default: My project. Ignored with invite_token.
invite_token string no RevenueDot extension. The token from an invite link (/invite?token=...).

Example request

Shell
curl -s -X POST "$REVENUEDOT_URL/auth/signup" \
  -H "Content-Type: application/json" -d '{"email":"dev@example.com","password":"change-me-please","project_name":"My app"}'

Responses

  • 201: Signed up and signed in. With an invite, project_id is the project joined.
  • 400: Invalid email or password, or an invite that is not valid (invite_invalid) or for another address (invite_email_mismatch).
  • 403: Sign-up is closed: the server has an owner already.
  • 409: The email is taken.

Example 201 response:

JSON
{
  "ok": true
}

Sign in#

POST /auth/login · Auth: none · RevenueDot extension

Request body (application/json)

Field Type Required Description
email string yes
password string yes

Example request

Shell
curl -s -X POST "$REVENUEDOT_URL/auth/login"

Responses

  • 200: Signed in; rd_session is set.
  • 400: Missing fields.
  • 401: Wrong email or password.

Example 200 response:

JSON
{
  "ok": true
}

Sign out#

POST /auth/logout · Auth: dashboard session · RevenueDot extension

Example request

Shell
curl -s -X POST "$REVENUEDOT_URL/auth/logout"

Responses

  • 200: Signed out.

Example 200 response:

JSON
{
  "ok": true
}

The signed-in user and their projects#

GET /auth/me · Auth: dashboard session · RevenueDot extension

Example request

Shell
curl -s "$REVENUEDOT_URL/auth/me"

Responses

  • 200: The user.
  • 401: Not signed in.

Example 200 response:

JSON
{
  "user": {
    "id": "usr_8k2m4q",
    "email": "dev@example.com",
    "name": "Dana",
    "email_verified": true,
    "alert_emails": true
  },
  "account": {
    "edition": "cloud",
    "plan": "free",
    "email_verification_required": false
  },
  "projects": []
}

Update account settings#

POST /auth/me · Auth: dashboard session · RevenueDot extension

The display name and whether the user gets alert emails for projects they administer. Send only the fields to change. A null or empty name clears it.

Request body (application/json)

Field Type Required Description
name string or null no
alert_emails boolean no False stops alert emails for every project.

Example request

Shell
curl -s -X POST "$REVENUEDOT_URL/auth/me" \
  -H "Content-Type: application/json" -d '{"alert_emails":false}'

Responses

  • 200: The updated user.
  • 400: Invalid field.
  • 401: Not signed in.

Example 200 response:

JSON
{
  "user": {
    "id": "usr_8k2m4q",
    "email": "dev@example.com",
    "name": "Dana",
    "email_verified": true,
    "alert_emails": false
  }
}

POST /auth/password/forgot · Auth: none · RevenueDot extension

Always answers 200 with the same body, whether or not an account uses the address, so the answer does not reveal who has an account. If one does, it gets a link to /reset-password that works once and expires after 1 hour.

Limits: 5 requests per IP address per 15 minutes (then 429), and 3 emails per address per hour (further requests answer 200 but send nothing). See I forgot my password.

Request body (application/json)

Field Type Required Description
email string yes

Example request

Shell
curl -s -X POST "$REVENUEDOT_URL/auth/password/forgot" \
  -H "Content-Type: application/json" -d '{"email":"dev@example.com"}'

Responses

  • 200: Accepted.
  • 400: Not a valid email address.
  • 429: Too many requests from this IP address.

Example 200 response:

JSON
{
  "ok": true,
  "message": "If an account uses this email, we sent it a link to reset the password. The link expires in 1 hour."
}

POST /auth/password/check · Auth: none · RevenueDot extension

Tells the reset page whether the link still works before the user types a new password. Does not use up the link.

Request body (application/json)

Field Type Required Description
token string yes The token from the reset link.

Example request

Shell
curl -s -X POST "$REVENUEDOT_URL/auth/password/check"

Responses

  • 200: Whether the link works.
  • 400: Missing token.

Example 200 response:

JSON
{
  "valid": false,
  "reason": "expired",
  "message": "This link has expired. Ask for a new one."
}

POST /auth/password/reset · Auth: none · RevenueDot extension

Sets the password, signs the user out on every device, marks the email as confirmed (the link proved the inbox) and signs this browser in with a new rd_session cookie. Every other open reset link of the user stops working.

Request body (application/json)

Field Type Required Description
token string yes
password string yes

Example request

Shell
curl -s -X POST "$REVENUEDOT_URL/auth/password/reset" \
  -H "Content-Type: application/json" -d '{"token":"…","password":"a-new-long-password"}'

Responses

  • 200: Password changed and signed in.
  • 400: The password is too short or too long, or the link is not valid (token_invalid with a reason).

Example 200 response:

JSON
{
  "ok": true
}

Confirm an email address#

POST /auth/email/verify · Auth: none · RevenueDot extension

RevenueDot Cloud only: the link in the confirmation email sent at sign-up (valid 24 hours, works once). Self-hosted servers treat every account as confirmed.

Request body (application/json)

Field Type Required Description
token string yes The token from the confirmation link.

Example request

Shell
curl -s -X POST "$REVENUEDOT_URL/auth/email/verify"

Responses

  • 200: Confirmed.
  • 400: The link is not valid (token_invalid with a reason).

Example 200 response:

JSON
{
  "ok": true,
  "email": "dev@example.com"
}

Send a new confirmation email#

POST /auth/email/verify/resend · Auth: dashboard session · RevenueDot extension

Up to 5 per user per hour. An account that is already confirmed gets already_verified: true and no email.

Example request

Shell
curl -s -X POST "$REVENUEDOT_URL/auth/email/verify/resend"

Responses

  • 200: Sent, or already confirmed.
  • 401: Not signed in.
  • 429: Too many emails this hour.
  • 502: The mail server did not accept the email.

Example 200 response:

JSON
{
  "ok": true,
  "email": "dev@example.com"
}

Look up an invite#

GET /auth/invites/{token} · Auth: none · RevenueDot extension

What the invite page shows: the project, the role, who sent it and whether the invited address has an account already (sign in and accept, or sign up with invite_token).

Path parameters

Name Type Required Description
token string yes The token from the invite link (/invite?token=...).

Example request

Shell
curl -s "$REVENUEDOT_URL/auth/invites/$TOKEN"

Responses

  • 200: The invite.
  • 404: Not valid, expired, already accepted or revoked.

Example 200 response:

JSON
{
  "object": "invite",
  "email": "sam@example.com",
  "role": "developer",
  "project": {
    "id": "proj18pzzkao",
    "name": "My app"
  },
  "invited_by": {
    "name": "Dana",
    "email": "dev@example.com"
  },
  "expires_at": 1791405714000,
  "account_exists": false
}

Accept an invite#

POST /auth/invites/{token}/accept · Auth: dashboard session · RevenueDot extension

For a user who already has an account, signed in with the invited address. Adds them to the project with the invite's role; someone who is already a member keeps their role. Also marks their email as confirmed.

Path parameters

Name Type Required Description
token string yes The token from the invite link (/invite?token=...).

Example request

Shell
curl -s -X POST "$REVENUEDOT_URL/auth/invites/$TOKEN/accept"

Responses

  • 200: Joined.
  • 401: Not signed in.
  • 403: Signed in with another address.
  • 404: The invite is no longer valid.

Example 200 response:

JSON
{
  "ok": true,
  "project_id": "proj18pzzkao"
}

Members and invites#

Invite people to a project by email, change their role, remove them. Dashboard session only.

List open invites#

GET /v2/projects/{project_id}/invites · Auth: dashboard session · RevenueDot extension · Permissions: project_configuration:collaborators:read

Invites nobody has accepted or revoked, oldest first. Expired ones stay listed so an admin can resend them. Needs a dashboard session: secret API keys cannot manage members.

Path parameters

Name Type Required Description
project_id string yes Project id (proj...).

Example request

Shell
curl -s "$REVENUEDOT_URL/v2/projects/$PROJECT_ID/invites"

Responses

  • 200: Open invites. Returns a list of Invite.
  • 401: No API key, or an unknown one. Returns V2Error.
  • 403: The key lacks a permission, or a public key was used. Returns V2Error.
  • 404: Not found in this project (another project's ids also answer 404). Returns V2Error.

Example 200 response:

JSON
{
  "object": "list",
  "items": [
    {
      "object": "invite",
      "id": "inv_4f8k2m9q1x7z",
      "email": "sam@example.com",
      "role": "developer",
      "status": "pending",
      "invited_by": "usr_8k2m4q",
      "created_at": 1790800914012,
      "last_sent_at": 1790800914012,
      "expires_at": 1791405714012
    }
  ],
  "next_page": null,
  "url": "/v2/projects/proj18pzzkao/invites"
}

Invite someone by email#

POST /v2/projects/{project_id}/invites · Auth: dashboard session · RevenueDot extension

Admins only. Emails a link that lasts 7 days. Inviting an address that already has an open invite replaces it: the role changes, a new link goes out and the old one stops working. See Invite your team.

On RevenueDot Cloud the admin needs a confirmed email address. A project can send 50 invites (including resends) per day; after that the answer is 429. email_sent is false when the mail server refused the email; the invite still exists and can be resent.

Path parameters

Name Type Required Description
project_id string yes Project id (proj...).

Request body (application/json)

Field Type Required Description
email string yes
role admin, developer, viewer yes

Example request

Shell
curl -s -X POST "$REVENUEDOT_URL/v2/projects/$PROJECT_ID/invites" \
  -H "Content-Type: application/json" -d '{"email":"sam@example.com","role":"developer"}'

Responses

  • 201: The invite.
  • 400: The request is invalid. Returns V2Error.
  • 401: No API key, or an unknown one. Returns V2Error.
  • 403: The key lacks a permission, or a public key was used. Returns V2Error.
  • 404: Not found in this project (another project's ids also answer 404). Returns V2Error.
  • 409: It already exists, or it conflicts with another object. Returns V2Error.
  • 429: Too many requests. Retry later. Returns V2Error.

Example 201 response:

JSON
{
  "object": "invite",
  "id": "inv_4f8k2m9q1x7z",
  "email": "sam@example.com",
  "role": "developer",
  "status": "pending",
  "invited_by": "usr_8k2m4q",
  "created_at": 1790800914012,
  "last_sent_at": 1790800914012,
  "expires_at": 1791405714012,
  "email_sent": true
}

Resend an invite#

POST /v2/projects/{project_id}/invites/{invite_id}/actions/resend · Auth: dashboard session · RevenueDot extension

Admins only. Sends a new link valid for 7 more days; the old link stops working. Works on expired invites. Counts toward the 50 invites per project per day.

Path parameters

Name Type Required Description
project_id string yes Project id (proj...).
invite_id string yes Invite id (inv_...).

Example request

Shell
curl -s -X POST "$REVENUEDOT_URL/v2/projects/$PROJECT_ID/invites/$INVITE_ID/actions/resend"

Responses

  • 200: The invite.
  • 401: No API key, or an unknown one. Returns V2Error.
  • 403: The key lacks a permission, or a public key was used. Returns V2Error.
  • 404: Not found in this project (another project's ids also answer 404). Returns V2Error.
  • 429: Too many requests. Retry later. Returns V2Error.

Revoke an invite#

DELETE /v2/projects/{project_id}/invites/{invite_id} · Auth: dashboard session · RevenueDot extension

Admins only. The link stops working at once.

Path parameters

Name Type Required Description
project_id string yes Project id (proj...).
invite_id string yes Invite id (inv_...).

Example request

Shell
curl -s -X DELETE "$REVENUEDOT_URL/v2/projects/$PROJECT_ID/invites/$INVITE_ID"

Responses

  • 200: Deleted. Returns Deleted.
  • 401: No API key, or an unknown one. Returns V2Error.
  • 403: The key lacks a permission, or a public key was used. Returns V2Error.
  • 404: Not found in this project (another project's ids also answer 404). Returns V2Error.

Example 200 response:

JSON
{
  "object": "invite",
  "id": "…",
  "deleted_at": 1790801342625
}

Change a member's role#

POST /v2/projects/{project_id}/collaborators/{user_id} · Auth: dashboard session · RevenueDot extension

Admins only. A project always keeps at least one admin, so the last admin cannot be demoted (400). The response uses RevenueCat's role names: viewer comes back as read_only.

Path parameters

Name Type Required Description
project_id string yes Project id (proj...).
user_id string yes The member's user id (the collaborator id).

Request body (application/json)

Field Type Required Description
role admin, developer, viewer yes

Example request

Shell
curl -s -X POST "$REVENUEDOT_URL/v2/projects/$PROJECT_ID/collaborators/$USER_ID" \
  -H "Content-Type: application/json" -d '{"role":"viewer"}'

Responses

  • 200: The member. Returns Collaborator.
  • 400: The request is invalid. Returns V2Error.
  • 401: No API key, or an unknown one. Returns V2Error.
  • 403: The key lacks a permission, or a public key was used. Returns V2Error.
  • 404: Not found in this project (another project's ids also answer 404). Returns V2Error.

Example 200 response:

JSON
{
  "object": "collaborator",
  "id": "usr_3n7p1x",
  "name": "Sam",
  "email": "sam@example.com",
  "role": "read_only",
  "accepted_at": 1790800914012,
  "has_mfa": false
}

Remove a member, or leave the project#

DELETE /v2/projects/{project_id}/collaborators/{user_id} · Auth: dashboard session · RevenueDot extension

Any member can remove themselves. Removing someone else takes an admin. The last admin cannot leave or be removed (422): make someone else an admin first, or delete the project.

Path parameters

Name Type Required Description
project_id string yes Project id (proj...).
user_id string yes The member's user id. Your own id leaves the project.

Example request

Shell
curl -s -X DELETE "$REVENUEDOT_URL/v2/projects/$PROJECT_ID/collaborators/$USER_ID"

Responses

  • 200: Deleted. Returns Deleted.
  • 401: No API key, or an unknown one. Returns V2Error.
  • 403: The key lacks a permission, or a public key was used. Returns V2Error.
  • 404: Not found in this project (another project's ids also answer 404). Returns V2Error.
  • 422: The request is valid but cannot be done in this state or for this store. Returns V2Error.

Example 200 response:

JSON
{
  "object": "collaborator",
  "id": "…",
  "deleted_at": 1790801342625
}

Project settings#

Project name, transfer behaviour and deletion.

Get a project with its settings#

GET /v2/projects/{project_id} · Auth: secret key or dashboard session · RevenueDot extension · Permissions: project_configuration:projects:read

Path parameters

Name Type Required Description
project_id string yes Project id (proj...).

Example request

Shell
curl -s "$REVENUEDOT_URL/v2/projects/$PROJECT_ID" -H "Authorization: Bearer $SECRET_KEY"

Responses

  • 200: The project. Returns ProjectSettings.
  • 401: No API key, or an unknown one. Returns V2Error.
  • 403: The key lacks a permission, or a public key was used. Returns V2Error.
  • 404: Not found in this project (another project's ids also answer 404). Returns V2Error.

Example 200 response:

JSON
{
  "object": "project",
  "id": "proj18pzzkao",
  "name": "My app",
  "created_at": 1790800900675,
  "icon_url": null,
  "icon_url_large": null,
  "transfer_behavior": "transfer",
  "sandbox_transfer_behavior": null
}

Update a project's name and transfer behaviour#

POST /v2/projects/{project_id} · Auth: secret key or dashboard session · RevenueDot extension · Permissions: project_configuration:projects:read_write

See who owns a restored purchase.

Path parameters

Name Type Required Description
project_id string yes Project id (proj...).

Request body (application/json)

Field Type Required Description
name string no
transfer_behavior transfer, transfer_if_no_active, keep, share no
sandbox_transfer_behavior transfer, transfer_if_no_active, keep, share, null no

Example request

Shell
curl -s -X POST "$REVENUEDOT_URL/v2/projects/$PROJECT_ID" -H "Authorization: Bearer $SECRET_KEY" \
  -H "Content-Type: application/json" -d '{"transfer_behavior":"transfer_if_no_active"}'

Responses

  • 200: The project. Returns ProjectSettings.
  • 400: The request is invalid. Returns V2Error.
  • 401: No API key, or an unknown one. Returns V2Error.
  • 403: The key lacks a permission, or a public key was used. Returns V2Error.
  • 404: Not found in this project (another project's ids also answer 404). Returns V2Error.

Delete a project and everything in it#

DELETE /v2/projects/{project_id} · Auth: dashboard session · RevenueDot extension · Permissions: project_configuration:projects:read_write

Only a project admin signed in to the dashboard can do this. Apps, catalog, customers, purchases, events and webhooks are deleted. Cannot be undone.

Path parameters

Name Type Required Description
project_id string yes Project id (proj...).

Example request

Shell
curl -s -X DELETE "$REVENUEDOT_URL/v2/projects/$PROJECT_ID"

Responses

  • 200: Deleted. Returns Deleted.
  • 401: No API key, or an unknown one. Returns V2Error.
  • 403: The key lacks a permission, or a public key was used. Returns V2Error.
  • 404: Not found in this project (another project's ids also answer 404). Returns V2Error.

Example 200 response:

JSON
{
  "object": "project",
  "id": "…",
  "deleted_at": 1790801342625
}

Store setup#

Notification URLs, credential checks, setup health and App Store mass extensions.

Store setup state of an app#

GET /v2/projects/{project_id}/apps/{app_id}/store_settings · Auth: secret key or dashboard session · RevenueDot extension · Permissions: project_configuration:apps:read

The notification URL to paste into App Store Connect or Pub/Sub, the notification status, the forwarding URL and which credentials are set. Never a secret.

Path parameters

Name Type Required Description
project_id string yes Project id (proj...).
app_id string yes App id (app...).

Example request

Shell
curl -s "$REVENUEDOT_URL/v2/projects/$PROJECT_ID/apps/$APP_ID/store_settings" -H "Authorization: Bearer $SECRET_KEY"

Responses

  • 200: The settings. Returns StoreSettings.
  • 401: No API key, or an unknown one. Returns V2Error.
  • 403: The key lacks a permission, or a public key was used. Returns V2Error.
  • 404: Not found in this project (another project's ids also answer 404). Returns V2Error.

Check store credentials with Apple or Google#

POST /v2/projects/{project_id}/apps/{app_id}/actions/verify_credentials · Auth: secret key or dashboard session · RevenueDot extension · Permissions: project_configuration:apps:read

Makes one harmless call to the App Store Server API or the Play Developer API. Values in the body are checked before you save them; missing values fall back to the saved ones.

Path parameters

Name Type Required Description
project_id string yes Project id (proj...).
app_id string yes App id (app...).

Request body (application/json)

Field Type Required Description
app_store object no
app_store.bundle_id string or null no
app_store.subscription_private_key string or null no
app_store.subscription_key_id string or null no
app_store.subscription_key_issuer string or null no
mac_app_store object no
mac_app_store.bundle_id string or null no
mac_app_store.subscription_private_key string or null no
mac_app_store.subscription_key_id string or null no
mac_app_store.subscription_key_issuer string or null no
play_store object no
play_store.package_name string or null no
play_store.play_service_account_credentials_json string or object or null no

Example request

Shell
curl -s -X POST "$REVENUEDOT_URL/v2/projects/$PROJECT_ID/apps/$APP_ID/actions/verify_credentials" -H "Authorization: Bearer $SECRET_KEY" \
  -H "Content-Type: application/json" -d '{}'

Responses

  • 200: The result. Returns CredentialsCheck.
  • 400: The request is invalid. Returns V2Error.
  • 401: No API key, or an unknown one. Returns V2Error.
  • 403: The key lacks a permission, or a public key was used. Returns V2Error.
  • 404: Not found in this project (another project's ids also answer 404). Returns V2Error.

Example 200 response:

JSON
{
  "object": "credentials_check",
  "app_id": "appugfw01uy",
  "store": "app_store",
  "status": "invalid",
  "valid": false,
  "message": "No in-app purchase key yet. Add the .p8 file, the key ID and the issuer ID.",
  "checked_at": 1790801342700
}

Extend every active App Store subscriber of a product#

POST /v2/projects/{project_id}/apps/{app_id}/actions/mass_extend · Auth: secret key or dashboard session · RevenueDot extension · Permissions: customer_information:subscriptions:read_write

Asks Apple to extend renewal dates for all active subscribers of product_id. Apple then sends one notification per subscription, which records SUBSCRIPTION_EXTENDED.

Path parameters

Name Type Required Description
project_id string yes Project id (proj...).
app_id string yes App id (app...).

Request body (application/json)

Field Type Required Description
product_id string yes
extend_by_days integer yes
extend_reason_code undeclared, customer_satisfaction, other, service_issue_or_outage yes
storefront_country_codes array of string no
environment production, sandbox no

Example request

Shell
curl -s -X POST "$REVENUEDOT_URL/v2/projects/$PROJECT_ID/apps/$APP_ID/actions/mass_extend" -H "Authorization: Bearer $SECRET_KEY" \
  -H "Content-Type: application/json" -d '{"product_id":"pro_monthly","extend_by_days":3,"extend_reason_code":"service_issue_or_outage"}'

Responses

  • 202: Accepted by Apple. Returns MassExtension.
  • 400: The request is invalid. Returns V2Error.
  • 401: No API key, or an unknown one. Returns V2Error.
  • 403: The key lacks a permission, or a public key was used. Returns V2Error.
  • 404: Not found in this project (another project's ids also answer 404). Returns V2Error.
  • 422: The request is valid but cannot be done in this state or for this store. Returns V2Error.
  • 503: The store could not be reached. Retry later. Returns V2Error.

Status of a mass extension#

GET /v2/projects/{project_id}/apps/{app_id}/mass_extensions/{request_id} · Auth: secret key or dashboard session · RevenueDot extension · Permissions: customer_information:subscriptions:read

Path parameters

Name Type Required Description
project_id string yes Project id (proj...).
app_id string yes App id (app...).
request_id string yes The id from the mass extend answer.

Query parameters

Name Type Required Description
product_id string yes
environment production, sandbox no

Example request

Shell
curl -s "$REVENUEDOT_URL/v2/projects/$PROJECT_ID/apps/$APP_ID/mass_extensions/$REQUEST_ID" -H "Authorization: Bearer $SECRET_KEY"

Responses

  • 200: The status. Returns MassExtension.
  • 400: The request is invalid. Returns V2Error.
  • 401: No API key, or an unknown one. Returns V2Error.
  • 403: The key lacks a permission, or a public key was used. Returns V2Error.
  • 404: Not found in this project (another project's ids also answer 404). Returns V2Error.
  • 422: The request is valid but cannot be done in this state or for this store. Returns V2Error.
  • 503: The store could not be reached. Retry later. Returns V2Error.

Setup health#

GET /v2/projects/{project_id}/setup_health · Auth: secret key or dashboard session · RevenueDot extension · Permissions: project_configuration:apps:read

Per app: the notification URL and whether notifications arrive. For webhooks: deliveries in the last 24 hours and failing endpoints. Also the SDK versions calling the server.

Path parameters

Name Type Required Description
project_id string yes Project id (proj...).

Example request

Shell
curl -s "$REVENUEDOT_URL/v2/projects/$PROJECT_ID/setup_health" -H "Authorization: Bearer $SECRET_KEY"

Responses

  • 200: Setup health. Returns SetupHealth.
  • 401: No API key, or an unknown one. Returns V2Error.
  • 403: The key lacks a permission, or a public key was used. Returns V2Error.
  • 404: Not found in this project (another project's ids also answer 404). Returns V2Error.

API keys#

Secret keys for the REST API.

List secret keys#

GET /v2/projects/{project_id}/api_keys · Auth: secret key or dashboard session · RevenueDot extension · Permissions: project_configuration:api_keys:read

Never returns the key itself.

Path parameters

Name Type Required Description
project_id string yes Project id (proj...).

Query parameters

Name Type Required Description
limit integer no Page size. Values outside 1-100 are clamped, not rejected.
starting_after string no Id of the last item of the previous page. Use next_page instead of building it.

Example request

Shell
curl -s "$REVENUEDOT_URL/v2/projects/$PROJECT_ID/api_keys" -H "Authorization: Bearer $SECRET_KEY"

Responses

  • 200: A page of results. Returns a list of ApiKey.
  • 401: No API key, or an unknown one. Returns V2Error.
  • 403: The key lacks a permission, or a public key was used. Returns V2Error.
  • 404: Not found in this project (another project's ids also answer 404). Returns V2Error.

Create a secret key#

POST /v2/projects/{project_id}/api_keys · Auth: secret key or dashboard session · RevenueDot extension · Permissions: project_configuration:api_keys:read_write

The answer includes key once. permissions default to ["*"]. A key cannot create a key with permissions it does not hold.

Path parameters

Name Type Required Description
project_id string yes Project id (proj...).

Request body (application/json)

Field Type Required Description
name string yes
permissions array of string no

Example request

Shell
curl -s -X POST "$REVENUEDOT_URL/v2/projects/$PROJECT_ID/api_keys" -H "Authorization: Bearer $SECRET_KEY" \
  -H "Content-Type: application/json" -d '{"name":"Backend (read only)","permissions":["customer_information:customers:read"]}'

Responses

  • 201: The key. Returns ApiKey.
  • 400: The request is invalid. Returns V2Error.
  • 401: No API key, or an unknown one. Returns V2Error.
  • 403: The key lacks a permission, or a public key was used. Returns V2Error.
  • 404: Not found in this project (another project's ids also answer 404). Returns V2Error.

Example 201 response:

JSON
{
  "object": "api_key",
  "id": "key_08ec817fce",
  "name": "Backend (read only)",
  "prefix": "sk_08ec",
  "permissions": [
    "customer_information:customers:read"
  ],
  "created_at": 1790801342634,
  "last_used_at": null,
  "key": "sk_08ec817fceead27005772bb943c2bd225850eb931c9835f0"
}

Delete a secret key#

DELETE /v2/projects/{project_id}/api_keys/{key_id} · Auth: secret key or dashboard session · RevenueDot extension · Permissions: project_configuration:api_keys:read_write

Path parameters

Name Type Required Description
project_id string yes Project id (proj...).
key_id string yes Key id (key_...).

Example request

Shell
curl -s -X DELETE "$REVENUEDOT_URL/v2/projects/$PROJECT_ID/api_keys/$KEY_ID" -H "Authorization: Bearer $SECRET_KEY"

Responses

  • 200: Deleted. Returns Deleted.
  • 401: No API key, or an unknown one. Returns V2Error.
  • 403: The key lacks a permission, or a public key was used. Returns V2Error.
  • 404: Not found in this project (another project's ids also answer 404). Returns V2Error.

Example 200 response:

JSON
{
  "object": "api_key",
  "id": "…",
  "deleted_at": 1790801342625
}

Webhook deliveries#

Delivery log, manual retry and test events.

Send a TEST event to one webhook#

POST /v2/projects/{project_id}/integrations/webhooks/{webhook_integration_id}/test · Auth: secret key or dashboard session · RevenueDot extension · Permissions: project_configuration:integrations:read_write

Queues a purchase-shaped TEST event, signed and retried like any delivery. The webhook's filters do not apply. A paused webhook (enabled false) answers 422.

Path parameters

Name Type Required Description
project_id string yes Project id (proj...).
webhook_integration_id string yes Webhook id.

Example request

Shell
curl -s -X POST "$REVENUEDOT_URL/v2/projects/$PROJECT_ID/integrations/webhooks/$WEBHOOK_INTEGRATION_ID/test" -H "Authorization: Bearer $SECRET_KEY"

Responses

  • 201: The queued delivery. Returns WebhookDelivery.
  • 401: No API key, or an unknown one. Returns V2Error.
  • 403: The key lacks a permission, or a public key was used. Returns V2Error.
  • 404: Not found in this project (another project's ids also answer 404). Returns V2Error.
  • 422: The request is valid but cannot be done in this state or for this store. Returns V2Error.

Whether each webhook is enabled#

GET /v2/projects/{project_id}/webhooks · Auth: secret key or dashboard session · RevenueDot extension · Permissions: project_configuration:integrations:read

RevenueCat's webhook object has no enabled field, so it is read here. Set it with POST .../integrations/webhooks/{id}.

Path parameters

Name Type Required Description
project_id string yes Project id (proj...).

Example request

Shell
curl -s "$REVENUEDOT_URL/v2/projects/$PROJECT_ID/webhooks" -H "Authorization: Bearer $SECRET_KEY"

Responses

  • 200: A page of results. Returns a list of WebhookState.
  • 401: No API key, or an unknown one. Returns V2Error.
  • 403: The key lacks a permission, or a public key was used. Returns V2Error.
  • 404: Not found in this project (another project's ids also answer 404). Returns V2Error.

Delivery log of a webhook#

GET /v2/projects/{project_id}/webhooks/{webhook_id}/deliveries · Auth: secret key or dashboard session · RevenueDot extension · Permissions: project_configuration:integrations:read

Newest first.

Path parameters

Name Type Required Description
project_id string yes Project id (proj...).
webhook_id string yes Webhook id (wh_...).

Query parameters

Name Type Required Description
status pending, delivered, failed no
limit integer no Page size. Values outside 1-100 are clamped, not rejected.
starting_after string no Id of the last item of the previous page. Use next_page instead of building it.

Example request

Shell
curl -s "$REVENUEDOT_URL/v2/projects/$PROJECT_ID/webhooks/$WEBHOOK_ID/deliveries" -H "Authorization: Bearer $SECRET_KEY"

Responses

  • 200: A page of results. Returns a list of WebhookDelivery.
  • 400: The request is invalid. Returns V2Error.
  • 401: No API key, or an unknown one. Returns V2Error.
  • 403: The key lacks a permission, or a public key was used. Returns V2Error.
  • 404: Not found in this project (another project's ids also answer 404). Returns V2Error.

Retry a delivery now#

POST /v2/projects/{project_id}/webhooks/{webhook_id}/deliveries/{delivery_id}/retry · Auth: secret key or dashboard session · RevenueDot extension · Permissions: project_configuration:integrations:read_write

Path parameters

Name Type Required Description
project_id string yes Project id (proj...).
webhook_id string yes Webhook id.
delivery_id string yes Delivery id.

Example request

Shell
curl -s -X POST "$REVENUEDOT_URL/v2/projects/$PROJECT_ID/webhooks/$WEBHOOK_ID/deliveries/$DELIVERY_ID/retry" -H "Authorization: Bearer $SECRET_KEY"

Responses

  • 200: The delivery, queued. Returns WebhookDelivery.
  • 401: No API key, or an unknown one. Returns V2Error.
  • 403: The key lacks a permission, or a public key was used. Returns V2Error.
  • 404: Not found in this project (another project's ids also answer 404). Returns V2Error.

Event log#

Every recorded event and money movement.

Event log#

GET /v2/projects/{project_id}/events · Auth: secret key or dashboard session · RevenueDot extension · Permissions: customer_information:customers:read

Every event the project recorded, newest first. body is exactly what webhooks receive.

Path parameters

Name Type Required Description
project_id string yes Project id (proj...).

Query parameters

Name Type Required Description
type array of string no Event types (any case); repeat or comma-separate.
customer string no Any app user id of the customer.
environment production, sandbox no Only this environment. Default: both.
limit integer no Page size. Values outside 1-100 are clamped, not rejected.
starting_after string no Id of the last item of the previous page. Use next_page instead of building it.

Example request

Shell
curl -s "$REVENUEDOT_URL/v2/projects/$PROJECT_ID/events" -H "Authorization: Bearer $SECRET_KEY"

Responses

  • 200: A page of results. Returns a list of Event.
  • 400: The request is invalid. Returns V2Error.
  • 401: No API key, or an unknown one. Returns V2Error.
  • 403: The key lacks a permission, or a public key was used. Returns V2Error.
  • 404: Not found in this project (another project's ids also answer 404). Returns V2Error.

Transaction feed#

GET /v2/projects/{project_id}/transactions · Auth: secret key or dashboard session · RevenueDot extension · Permissions: customer_information:purchases:read

Every purchase, renewal, trial start, refund and refund reversal, newest first.

Path parameters

Name Type Required Description
project_id string yes Project id (proj...).

Query parameters

Name Type Required Description
customer string no Any app user id of the customer.
environment production, sandbox no Only this environment. Default: both.
limit integer no Page size. Values outside 1-100 are clamped, not rejected.
starting_after string no Id of the last item of the previous page. Use next_page instead of building it.

Example request

Shell
curl -s "$REVENUEDOT_URL/v2/projects/$PROJECT_ID/transactions" -H "Authorization: Bearer $SECRET_KEY"

Responses

  • 200: A page of results. Returns a list of Transaction.
  • 400: The request is invalid. Returns V2Error.
  • 401: No API key, or an unknown one. Returns V2Error.
  • 403: The key lacks a permission, or a public key was used. Returns V2Error.
  • 404: Not found in this project (another project's ids also answer 404). Returns V2Error.

Test Store#

Simulated purchases and lifecycles for development.

Simulate a Test Store purchase or lifecycle#

POST /v2/projects/{project_id}/test_purchases · Auth: secret key or dashboard session · RevenueDot extension · Permissions: customer_information:purchases:read_write

Runs a purchase through the same pipeline as an SDK receipt, so events, the transaction ledger and webhooks come out as they would. Scenarios: purchase, trial, trial_conversion, renewal, cancel, billing_issue, refund, expire. See the Test Store guide.

Path parameters

Name Type Required Description
project_id string yes Project id (proj...).

Request body (application/json)

Field Type Required Description
app_user_id string yes
product_id string yes Product id or store identifier of a Test Store product.
app_id string no Test Store app; default the project's first.
price number no
currency string no Three letters; default USD.
purchased_at integer no Start, epoch milliseconds. Not with offset_days.
presented_offering_id string no
scenario purchase, trial, trial_conversion, renewal, cancel, billing_issue, refund, expire no
offset_days number no Days ago the scenario starts (0 to 730).
country_code string no ISO 3166-1 alpha-2, upper case.

Example request

Shell
curl -s -X POST "$REVENUEDOT_URL/v2/projects/$PROJECT_ID/test_purchases" -H "Authorization: Bearer $SECRET_KEY" \
  -H "Content-Type: application/json" -d '{"app_user_id":"user_renewal","product_id":"pro_monthly","scenario":"renewal","price":9.99}'

Responses

  • 201: What happened. Returns TestPurchase.
  • 400: The request is invalid. Returns V2Error.
  • 401: No API key, or an unknown one. Returns V2Error.
  • 403: The key lacks a permission, or a public key was used. Returns V2Error.
  • 404: Not found in this project (another project's ids also answer 404). Returns V2Error.
  • 422: The request is valid but cannot be done in this state or for this store. Returns V2Error.

Dashboard data#

Series and rows the dashboard shows.

Daily history of an overview metric#

GET /v2/projects/{project_id}/metrics/history · Auth: secret key or dashboard session · RevenueDot extension · Permissions: charts_metrics:overview:read

Path parameters

Name Type Required Description
project_id string yes Project id (proj...).

Query parameters

Name Type Required Description
metric active_trials, active_subscriptions, mrr, revenue, new_customers, active_users yes
days integer no
environment production, sandbox no

Example request

Shell
curl -s "$REVENUEDOT_URL/v2/projects/$PROJECT_ID/metrics/history" -H "Authorization: Bearer $SECRET_KEY"

Responses

  • 200: The history. Returns MetricHistory.
  • 400: The request is invalid. Returns V2Error.
  • 401: No API key, or an unknown one. Returns V2Error.
  • 403: The key lacks a permission, or a public key was used. Returns V2Error.
  • 404: Not found in this project (another project's ids also answer 404). Returns V2Error.

Dashboard rows for customers#

GET /v2/projects/{project_id}/customer_summaries · Auth: secret key or dashboard session · RevenueDot extension · Permissions: customer_information:customers:read

Revenue, entitlement names and prices per customer. Unknown ids are left out.

Path parameters

Name Type Required Description
project_id string yes Project id (proj...).

Query parameters

Name Type Required Description
ids string yes Up to 100 app user ids, comma separated or repeated.

Example request

Shell
curl -s "$REVENUEDOT_URL/v2/projects/$PROJECT_ID/customer_summaries" -H "Authorization: Bearer $SECRET_KEY"

Responses

  • 200: A page of results. Returns a list of CustomerSummary.
  • 400: The request is invalid. Returns V2Error.
  • 401: No API key, or an unknown one. Returns V2Error.
  • 403: The key lacks a permission, or a public key was used. Returns V2Error.
  • 404: Not found in this project (another project's ids also answer 404). Returns V2Error.

Migration import#

Bulk import from RevenueCat, used by the revenuedot import CLI.

Import customers with their purchases#

POST /v2/projects/{project_id}/import/customers · Auth: secret key or dashboard session · RevenueDot extension · Permissions: customer_information:customers:read_write

Up to 100 RevenueCat-shaped customers per call, each with aliases, attributes, subscriptions and one-time purchases. Writes state directly: no events and no webhooks unless emit_events is true. Keeps first-seen dates, original purchase dates and store transaction ids, and keys each subscription like the store adapters do (Apple original transaction id, Google purchase token), so later receipts and notifications update the imported row. Running the same import twice changes nothing. Google subscriptions without purchase_token are keyed needs_token_refresh:<order id> until a token is found. The revenuedot import CLI calls this; see the importer.

Path parameters

Name Type Required Description
project_id string yes Project id (proj...).

Request body (application/json)

Field Type Required Description
customers array of object yes
customers[].id string yes
customers[].aliases array of string no
customers[].first_seen_at integer no
customers[].last_seen_at integer no
customers[].last_seen_app_version string or null no
customers[].last_seen_country string or null no
customers[].last_seen_platform string or null no
customers[].attributes array of object no
customers[].attributes[].name string yes
customers[].attributes[].value string or null yes
customers[].attributes[].updated_at integer no
customers[].subscriptions array of object no
customers[].subscriptions[].source_id string no
customers[].subscriptions[].app_id string or null no
customers[].subscriptions[].store string yes
customers[].subscriptions[].product_identifier string yes
customers[].subscriptions[].environment production, sandbox no
customers[].subscriptions[].ownership purchased, family_shared no
customers[].subscriptions[].starts_at integer yes
customers[].subscriptions[].current_period_starts_at integer yes
customers[].subscriptions[].current_period_ends_at integer or null no
customers[].subscriptions[].status trialing, active, expired, in_grace_period, in_billing_retry, paused, unknown, incomplete yes
customers[].subscriptions[].auto_renewal_status will_renew, will_not_renew, will_change_product, will_pause, requires_price_increase_consent, has_already_renewed no
customers[].subscriptions[].store_subscription_identifier string yes
customers[].subscriptions[].original_transaction_id string or null no
customers[].subscriptions[].original_transaction_id_confirmed boolean no
customers[].subscriptions[].purchase_token string or null no
customers[].subscriptions[].period_type normal, trial, intro, promotional, prepaid no
customers[].subscriptions[].country string or null no
customers[].subscriptions[].price object or null no
customers[].subscriptions[].total_revenue_usd number or null no
customers[].subscriptions[].unsubscribe_detected_at integer or null no
customers[].subscriptions[].billing_issues_detected_at integer or null no
customers[].subscriptions[].grace_period_expires_at integer or null no
customers[].subscriptions[].refunded_at integer or null no
customers[].subscriptions[].auto_resume_at integer or null no
customers[].subscriptions[].entitlement_lookup_keys array of string no
customers[].subscriptions[].auto_renew_product_identifier string or null no
customers[].subscriptions[].transactions array of object no
customers[].purchases array of object no
customers[].purchases[].source_id string no
customers[].purchases[].app_id string or null no
customers[].purchases[].store string yes
customers[].purchases[].product_identifier string yes
customers[].purchases[].environment production, sandbox no
customers[].purchases[].purchased_at integer yes
customers[].purchases[].store_purchase_identifier string yes
customers[].purchases[].status owned, refunded no
customers[].purchases[].refunded_at integer or null no
customers[].purchases[].consumable boolean no
customers[].purchases[].price object or null no
customers[].purchases[].revenue_usd number or null no
customers[].purchases[].country string or null no
emit_events boolean no Default false.
resolve_store_ids boolean no Default true: use the app's store credentials to confirm Apple ids and find Google tokens.

Example request

Shell
curl -s -X POST "$REVENUEDOT_URL/v2/projects/$PROJECT_ID/import/customers" -H "Authorization: Bearer $SECRET_KEY" \
  -H "Content-Type: application/json" -d '{"customers":[{"id":"imported_1","aliases":["$RCAnonymousID:0f1e2d"],"first_seen_at":1735689600000,"attributes":[{"name":"$email","value":"ana@example.com"}],"subscriptions":[{"store":"test_store","app_id":"appvnrm0a5h","product_identifier":"pro_annual","starts_at":1735689600000,"current_period_starts_at":1767225600000,"current_period_ends_at":1798761600000,"status":"active","auto_renewal_status":"will_renew","store_subscription_identifier":"test_1767225600000_imported"}]}]}'

Responses

  • 200: A report per customer. Returns ImportResult.
  • 400: The request is invalid. Returns V2Error.
  • 401: No API key, or an unknown one. Returns V2Error.
  • 403: The key lacks a permission, or a public key was used. Returns V2Error.
  • 404: Not found in this project (another project's ids also answer 404). Returns V2Error.

Example 200 response:

JSON
{
  "object": "import_result",
  "emit_events": false,
  "customers": [
    {
      "id": "imported_1",
      "status": "created",
      "subscriptions": 1,
      "purchases": 0,
      "needs_token_refresh": 0,
      "notes": []
    }
  ]
}

Keep an app's existing SDK key#

POST /v2/projects/{project_id}/import/apps/{app_id}/public_key · Auth: secret key or dashboard session · RevenueDot extension · Permissions: project_configuration:apps:read_write

Sets the app's public key to the one your shipped app binaries already send (appl_..., goog_...), so old app versions work against RevenueDot. The prefix must match the app's store.

Path parameters

Name Type Required Description
project_id string yes Project id (proj...).
app_id string yes App id (app...).

Request body (application/json)

Field Type Required Description
public_key string yes

Example request

Shell
curl -s -X POST "$REVENUEDOT_URL/v2/projects/$PROJECT_ID/import/apps/$APP_ID/public_key" -H "Authorization: Bearer $SECRET_KEY" \
  -H "Content-Type: application/json" -d '{"public_key":"appl_AbCdEfGhIjKlMnOpQrStUvWxYz"}'

Responses

  • 200: The key. Returns PublicApiKey.
  • 400: The request is invalid. Returns V2Error.
  • 401: No API key, or an unknown one. Returns V2Error.
  • 403: The key lacks a permission, or a public key was used. Returns V2Error.
  • 404: Not found in this project (another project's ids also answer 404). Returns V2Error.
  • 409: It already exists, or it conflicts with another object. Returns V2Error.

What still needs attention after an import#

GET /v2/projects/{project_id}/import/status · Auth: secret key or dashboard session · RevenueDot extension · Permissions: customer_information:customers:read

Path parameters

Name Type Required Description
project_id string yes Project id (proj...).

Example request

Shell
curl -s "$REVENUEDOT_URL/v2/projects/$PROJECT_ID/import/status" -H "Authorization: Bearer $SECRET_KEY"

Responses

  • 200: Counts. Returns ImportStatus.
  • 401: No API key, or an unknown one. Returns V2Error.
  • 403: The key lacks a permission, or a public key was used. Returns V2Error.
  • 404: Not found in this project (another project's ids also answer 404). Returns V2Error.

Example 200 response:

JSON
{
  "object": "import_status",
  "customers": 13,
  "subscriptions": 10,
  "needs_token_refresh": 0,
  "needs_token_refresh_by_app": {}
}

OAuth for MCP clients#

OAuth 2.1 with PKCE so MCP clients can connect to one project without copying a key.

OAuth authorization server metadata#

GET /.well-known/oauth-authorization-server · Auth: none · RevenueDot extension

RFC 8414 metadata for MCP clients (Claude, ChatGPT, Cursor ...). Endpoints are built from this server's public origin.

Example request

Shell
curl -s "$REVENUEDOT_URL/.well-known/oauth-authorization-server"

Responses

  • 200: Metadata.

Example 200 response:

JSON
{
  "issuer": "https://revenuedot.example.com",
  "authorization_endpoint": "https://revenuedot.example.com/oauth/authorize",
  "token_endpoint": "https://revenuedot.example.com/oauth/token",
  "registration_endpoint": "https://revenuedot.example.com/oauth/register",
  "scopes_supported": [
    "project:read",
    "project:write"
  ],
  "response_types_supported": [
    "code"
  ],
  "response_modes_supported": [
    "query"
  ],
  "grant_types_supported": [
    "authorization_code"
  ],
  "token_endpoint_auth_methods_supported": [
    "none"
  ],
  "code_challenge_methods_supported": [
    "S256"
  ],
  "service_documentation": "https://revenuedot.app/docs/mcp"
}

Register an OAuth client#

POST /oauth/register · Auth: none · RevenueDot extension

Dynamic client registration (RFC 7591), public clients only. Redirect URIs must be https, http on localhost, or an app scheme such as cursor://.

Request body (application/json)

Field Type Required Description
redirect_uris array of string yes
client_name string no
grant_types array of string no

Example request

Shell
curl -s -X POST "$REVENUEDOT_URL/oauth/register" \
  -H "Content-Type: application/json" -d '{"client_name":"Claude","redirect_uris":["https://claude.ai/api/mcp/auth_callback"]}'

Responses

  • 201: The client.
  • 400: Invalid metadata or redirect URI.

GET /oauth/authorize · Auth: none · RevenueDot extension

An HTML page. The user signs in to the dashboard (the session cookie is reused), picks one project and read or read-write access. PKCE with S256 is required.

Query parameters

Name Type Required Description
response_type string no
client_id string no
redirect_uri string no
state string no
scope string no
code_challenge string no
code_challenge_method string no
resource string no

Example request

Shell
curl -s "$REVENUEDOT_URL/oauth/authorize"

Responses

  • 200: The consent page.
  • 302: Back to the client with an error.
  • 400: Unknown client or redirect URI.

POST /oauth/authorize · Auth: dashboard session · RevenueDot extension

The consent form posts here. On allow, redirects to the client's redirect URI with a one-time code (valid 10 minutes).

Request body (application/x-www-form-urlencoded)

Field Type Required Description
decision allow, deny no
project_id string no
access project:read, project:write no
csrf string no

Example request

Shell
curl -s -X POST "$REVENUEDOT_URL/oauth/authorize"

Responses

  • 302: Redirect with code and state, or with error.
  • 403: The form expired.

Exchange a code for an access token#

POST /oauth/token · Auth: none · RevenueDot extension

authorization_code grant with PKCE. The access token is a secret API key (sk_...) bound to the chosen project with the approved permissions. It does not expire; revoke it on the project's API keys page.

Request body (application/x-www-form-urlencoded)

Field Type Required Description
grant_type "authorization_code" yes
code string yes
code_verifier string yes
client_id string no
redirect_uri string no

Example request

Shell
curl -s -X POST "$REVENUEDOT_URL/oauth/token"

Responses

  • 200: The token.
  • 400: Invalid grant or request.

Objects#

The shapes the operations above send and return.

ActiveEntitlement#

Field Type Required Description
object "customer.active_entitlement" yes
entitlement_id string yes Entitlement id (entl...), not the lookup key.
expires_at integer or null yes When access ends. Epoch milliseconds, or null.

ApiKey#

Field Type Required Description
object "api_key" yes
id string yes
name string yes
prefix string yes First 7 characters of the key.
permissions array of string yes Scopes; * is every scope.
created_at integer yes Creation time. Epoch milliseconds.
last_used_at integer or null yes Last use, updated at most once a minute. Epoch milliseconds, or null.
key string no The secret key (sk_...). Only in the answer that creates it.

App#

Only the object for the app's own type is present. Store secrets are never returned.

Field Type Required Description
object "app" yes
id string yes App id (app...).
name string yes
created_at integer yes Creation time. Epoch milliseconds.
type amazon, app_store, mac_app_store, play_store, stripe, rc_billing, roku, paddle, test_store yes
project_id string yes
custom_url_scheme string no Derived from the public key.
app_store object no
app_store.bundle_id string no
app_store.app_store_connect_api_key_configured boolean no
app_store.subscription_key_configured boolean no True when the in-app purchase key (.p8, key id, issuer id) is set.
app_store.app_store_connect_vendor_number string or null no
mac_app_store object no
mac_app_store.bundle_id string no
play_store object no
play_store.package_name string no
play_store.play_service_account_credentials_configured boolean no
amazon object no
amazon.package_name string no
stripe object no
stripe.stripe_account_id string or null no
rc_billing object no
rc_billing.stripe_account_id string or null no
rc_billing.seller_company_name string no
rc_billing.app_name string no
rc_billing.support_email string or null no
rc_billing.default_currency string no
roku object no
roku.roku_channel_id string or null no
roku.roku_channel_name string or null no
paddle object no
paddle.paddle_is_sandbox boolean no
paddle.paddle_api_key null no

Collaborator#

Field Type Required Description
object "collaborator" yes
id string yes
name string or null no
email string yes
role admin, developer, read_only yes RevenueCat's role names. read_only is the dashboard's Viewer role.
accepted_at integer no When the user joined. Epoch milliseconds.
has_mfa boolean no Always false.

CredentialsCheck#

Field Type Required Description
object "credentials_check" yes
app_id string yes
store string yes
status valid, invalid, unreachable yes
valid boolean yes
message string yes What to do next, in plain words.
checked_at integer yes Checked at. Epoch milliseconds.
key_id string no
client_email string or null no

Customer#

active_entitlements and experiment are present on single-customer answers; attributes only with expand=attributes.

Field Type Required Description
object "customer" yes
id string yes The customer's original app user id.
project_id string yes
first_seen_at integer yes First seen. Epoch milliseconds.
last_seen_at integer or null yes Last seen. Epoch milliseconds, or null.
last_seen_app_version string or null no
last_seen_country string or null no
last_seen_platform string or null no
last_seen_platform_version null no
active_entitlements object no
active_entitlements.object "list" yes
active_entitlements.items array of ActiveEntitlement yes
active_entitlements.next_page string or null yes Path of the next page, or null on the last page.
active_entitlements.url string yes Path of this list.
experiment null no
attributes object no
attributes.object "list" yes
attributes.items array of CustomerAttribute yes
attributes.next_page string or null yes Path of the next page, or null on the last page.
attributes.url string yes Path of this list.

CustomerAttribute#

Field Type Required Description
object "customer.attribute" yes
name string yes
value string yes
updated_at integer yes Last update. Epoch milliseconds.

CustomerSummary#

Field Type Required Description
object "customer_summary" yes
id string yes The id you asked for.
original_app_user_id string yes
aliases array of string no
total_revenue_in_usd number no
sandbox_revenue_in_usd number no
country string or null no
platform string or null no
stores array of string no
offering_override string or null no
active_entitlements array of object no
granted_entitlements array of object no
subscriptions array of object no
purchases array of object no

Deleted#

Field Type Required Description
object string yes The deleted object's type.
id string yes
deleted_at integer yes When it was deleted. Epoch milliseconds.

Entitlement#

Field Type Required Description
object "entitlement" yes
id string yes Entitlement id (entl...).
project_id string yes
lookup_key string yes What apps check, for example pro.
display_name string yes
created_at integer yes Creation time. Epoch milliseconds.
state active, inactive yes
products object no
products.object "list" yes
products.items array of Product yes
products.next_page string or null yes Path of the next page, or null on the last page.
products.url string yes Path of this list.

Event#

Field Type Required Description
object "event" yes
id string yes
type string yes
environment production, sandbox yes
app_id string or null no
customer_id string or null no Original app user id.
app_user_id string or null no
occurred_at integer yes When it happened. Epoch milliseconds.
created_at integer no Recorded. Epoch milliseconds.
body object yes The webhook event object, exactly as webhooks receive it.

ImportResult#

Field Type Required Description
object "import_result" yes
emit_events boolean yes
customers array of object yes
customers[].id string no
customers[].status created, updated, merged no
customers[].subscriptions integer no
customers[].purchases integer no
customers[].needs_token_refresh integer no
customers[].notes array of string no

ImportStatus#

Field Type Required Description
object "import_status" yes
customers integer yes
subscriptions integer yes
needs_token_refresh integer yes Google Play subscriptions still waiting for their purchase token.
needs_token_refresh_by_app object yes

IndicativePrice#

Field Type Required Description
object "indicative_price" yes
currency string yes ISO 4217 code.
country null yes
amount_micros integer yes Price in micros: 9.99 is 9990000.

Invite#

Field Type Required Description
object "invite" yes
id string yes inv_...
email string yes The invited address, lowercased.
role admin, developer, viewer yes The role the person gets when they accept.
status pending, expired, accepted, revoked yes Lists only show pending and expired. An expired invite can be resent.
invited_by string or null no User id of the admin who last sent it.
created_at integer no When it was created. Epoch milliseconds.
last_sent_at integer no When the last email went out. Epoch milliseconds.
expires_at integer yes When the link stops working: 7 days after it was last sent. Epoch milliseconds.

MassExtension#

Field Type Required Description
object "subscription_mass_extension" yes
id string yes Request id.
app_id string yes
product_id string yes
environment production, sandbox yes
extend_by_days integer no
extend_reason_code string no
storefront_country_codes array of string no
complete boolean yes
completed_at integer or null no
succeeded_count integer or null no
failed_count integer or null no
requested_at integer no

MetricHistory#

Field Type Required Description
object "metric_history" yes
id string yes
currency "USD" no
days integer yes
environment production, sandbox yes
resolution "day" no
value number no
previous_value number or null no
values array of object yes
values[].date string no YYYY-MM-DD
values[].value number no
last_updated_at integer no Computed at. Epoch milliseconds.

MonetaryAmount#

Field Type Required Description
currency string yes ISO 4217 currency code.
gross number yes Gross amount.
commission number yes Estimated store commission.
tax number yes Tax. Always 0 today.
proceeds number yes Gross minus commission.

Price#

Field Type Required Description
amount number yes Price in the purchase currency.
currency string yes ISO 4217 currency code.

Product#

Field Type Required Description
object "product" yes
id string yes Product id (prod...).
store_identifier string yes The store's product id. Google Play subscriptions use subscriptionId:basePlanId.
type subscription, one_time, consumable, non_consumable, non_renewing_subscription yes
state active, inactive yes
subscription object no
subscription.duration string or null no ISO 8601 period such as P1M.
subscription.grace_period_duration null no
subscription.trial_duration null no
one_time object no
one_time.is_consumable boolean or null no
created_at integer yes Creation time. Epoch milliseconds.
app_id string yes
display_name string or null yes
app App no Only the object for the app's own type is present. Store secrets are never returned.
indicative_price IndicativePrice or null no With expand=indicative_price: the Test Store price, or null.

Project#

Field Type Required Description
object "project" yes
id string yes Project id (proj...).
name string yes
created_at integer yes Creation time. Epoch milliseconds.
icon_url string or null no Always null.
icon_url_large string or null no Always null.

ProjectSettings#

Field Type Required Description
object "project" yes
id string yes Project id (proj...).
name string yes
created_at integer yes Creation time. Epoch milliseconds.
icon_url string or null no Always null.
icon_url_large string or null no Always null.
transfer_behavior transfer, transfer_if_no_active, keep, share yes What happens when a purchase already owned by another customer is restored. Default transfer.
sandbox_transfer_behavior transfer, transfer_if_no_active, keep, share, null yes Override for sandbox purchases; null uses transfer_behavior.

PublicApiKey#

Field Type Required Description
object "public_api_key" yes
id string yes
key string yes The key the SDK sends (appl_, goog_, test_ ...).
environment production, sandbox yes
app_id string yes
created_at integer yes Creation time. Epoch milliseconds.

Purchase#

Field Type Required Description
object "purchase" yes
id string yes
customer_id string yes
original_customer_id string no
product_id string yes
purchased_at integer yes Purchase time. Epoch milliseconds.
revenue_in_usd MonetaryAmount no
quantity integer no
status owned, refunded yes
presented_offering_id string or null no Offering the purchase was made from (its id, or the identifier the SDK sent when no such offering exists).
entitlements object no
entitlements.object "list" yes
entitlements.items array of Entitlement yes
entitlements.next_page string or null yes Path of the next page, or null on the last page.
entitlements.url string yes Path of this list.
environment production, sandbox yes
store string yes
store_purchase_identifier string no
ownership purchased no
country string no

SetupHealth#

Field Type Required Description
object "setup_health" yes
project_id string yes
checked_at integer yes Computed at. Epoch milliseconds.
apps array of object yes
apps[].id string no
apps[].name string no
apps[].type string no
apps[].notification_url string or null no Where the store must send notifications.
apps[].last_notification_at integer or null no Last notification processed for a known purchase (or the store's test). Epoch milliseconds, or null.
apps[].last_notification_received_at integer or null no Last notification received at all. Epoch milliseconds, or null.
apps[].last_notification_error object or null no
apps[].last_notification_error.at integer no
apps[].last_notification_error.type string or null no
apps[].last_notification_error.message string no
apps[].notification_status ready, failing, received, waiting no
apps[].credentials_configured boolean no
webhooks object yes
webhooks.total integer no
webhooks.attempted_24h integer no
webhooks.delivered_24h integer no
webhooks.failed_24h integer no
webhooks.pending integer no
webhooks.delivered_percent_24h number or null no
webhooks.failing array of object no
webhooks.failing[].id string no
webhooks.failing[].name string no
webhooks.failing[].url string no
webhooks.failing[].last_status integer or null no
webhooks.failing[].last_error string or null no
webhooks.failing[].last_attempt_at integer no
webhooks.failing[].delivery_status string no
sdk_versions array of object yes

StoreSettings#

Field Type Required Description
object "app_store_settings" yes
app_id string yes
type string yes
api_origin string yes This server as the outside world reaches it: the SDK's proxy URL.
notification_url string or null no App Store or Google Play notification URL for this app.
notification_forward_url string or null no Where notifications are copied during a dual run.
last_notification_at integer or null no Last notification processed for a known purchase. Epoch milliseconds, or null.
last_notification_error string or null no
last_notification_received_at integer or null no Last notification received. Epoch milliseconds, or null.
notification_status ready, failing, received, waiting yes
last_forward object or null no
last_forward.status integer no HTTP status of the forward; 0 means no answer.
last_forward.at integer no
track_new_purchases boolean no Apply notifications about purchases this server has never seen.
allow_unsigned_receipts boolean no Accept StoreKit 1 receipts without the in-app purchase key. Development only.
credentials object yes
credentials.subscription_key object no
credentials.subscription_key.configured boolean no
credentials.subscription_key.key_id string or null no
credentials.subscription_key.issuer_id string or null no
credentials.app_store_connect_api_key object no
credentials.app_store_connect_api_key.configured boolean no
credentials.app_store_connect_api_key.key_id string or null no
credentials.app_store_connect_api_key.issuer_id string or null no
credentials.app_store_connect_api_key.vendor_number string or null no
credentials.shared_secret object no
credentials.shared_secret.configured boolean no
credentials.play_service_account object no
credentials.play_service_account.configured boolean no
credentials.play_service_account.client_email string or null no
credentials.xcode_certificate object no
credentials.xcode_certificate.configured boolean no

Subscription#

Field Type Required Description
object "subscription" yes
id string yes Subscription id (sub_...).
customer_id string yes
original_customer_id string no
product_id string or null no Product id (prod...), null for promotional grants.
starts_at integer yes Start of the subscription. Epoch milliseconds.
current_period_starts_at integer no Start of the current period. Epoch milliseconds.
current_period_ends_at integer or null no End of the current period. Epoch milliseconds, or null.
ends_at integer or null no End of access. Epoch milliseconds, or null.
gives_access boolean yes
pending_payment boolean no
auto_renewal_status will_renew, will_not_renew, will_change_product, will_pause yes
status trialing, active, in_grace_period, in_billing_retry, paused, expired yes
total_revenue_in_usd MonetaryAmount no
presented_offering_id string or null no Offering the purchase was made from (its id, or the identifier the SDK sent when no such offering exists).
entitlements object no
entitlements.object "list" yes
entitlements.items array of Entitlement yes
entitlements.next_page string or null yes Path of the next page, or null on the last page.
entitlements.url string yes Path of this list.
environment production, sandbox yes
store string yes
store_subscription_identifier string no Latest store transaction id, order id or token.
ownership purchased, family_shared no
country string no ISO 3166-1 alpha-2, when known.
management_url null no

TestPurchase#

Field Type Required Description
object "test_purchase" yes
scenario string yes
store_transaction_id string yes The Test Store token (test__).
event_types array of string yes Events recorded, in order.
customer Customer yes active_entitlements and experiment are present on single-customer answers; attributes only with expand=attributes.
subscription Subscription or null no
purchase Purchase or null no

Transaction#

Field Type Required Description
object "transaction" yes
id string yes
customer_id string yes
app_id string or null no
store string yes
store_transaction_id string or null no
product_identifier string yes
kind purchase, renewal, trial, one_time, refund, refund_reversal yes
environment production, sandbox yes
purchased_at integer yes When the money moved. Epoch milliseconds.
expires_at integer or null no End of the period. Epoch milliseconds, or null.
revenue_in_usd number yes USD; negative for refunds.
price Price or null no
country string or null no

V2Error#

Field Type Required Description
object "error" yes
type parameter_error, resource_already_exists, resource_missing, idempotency_error, rate_limit_error, authentication_error, authorization_error, store_error, server_error, resource_locked_error, unprocessable_entity_error, invalid_request, entity_references_archived_entities yes
message string yes What went wrong.
param string no The request field at fault, when there is one.
doc_url string yes Link to the error's section of the errors page.
retryable boolean yes True when retrying the same request can succeed.

WebhookDelivery#

Field Type Required Description
object "webhook_delivery" yes
id string yes
webhook_integration_id string yes
event_id string yes
event_type string yes
status pending, delivered, failed yes
attempts integer yes
next_attempt_at integer or null no Next retry, when pending. Epoch milliseconds, or null.
response_status integer or null no HTTP status of the last attempt.
response_ms integer or null no Duration of the last attempt.
last_error string or null no
created_at integer no Queued at. Epoch milliseconds.

WebhookState#

Field Type Required Description
object "webhook_state" yes
id string yes Webhook id (wh_...).
enabled boolean yes False while deliveries are paused.