Revenue API

Push customer, charge, and subscription data from any payment provider into Himetrica. Manual revenue data integrates seamlessly with all existing revenue metrics — MRR, timeline, cohorts, top customers, and visitor linking.

Overview

The Revenue API lets you track revenue from payment providers that Himetrica doesn't natively integrate with (beyond Stripe, Shopify, and AbacatePay). Use it for:

  • Custom payment gateways or in-house billing
  • Offline or invoice-based payments
  • Marketplace payouts and platform fees
  • Webhook-driven revenue ingestion from any source

Base URL: https://app.himetrica.com/api/v1

How it works

A "manual" integration is automatically created for your project on the first API call. Customers, charges, and subscriptions are stored in the same tables as native integrations, so all dashboard charts and metrics include manual data with zero additional configuration.

Authentication

All Revenue API endpoints require a secret key passed in the X-API-Key header. Secret keys start with hm_sk_ and are the same keys used by the Server API.

Keep your secret key safe

Never expose the secret key in client-side code, public repositories, or browser-accessible files. Use environment variables on your server.

Rate Limiting

All Revenue API endpoints share a single rate limit:

ScopeLimitWindow
All /revenue/* endpoints500 requests1 minute

Rate limit headers (X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Reset) are included in every response.

Customers

POST/revenue/customers

Create a new customer. If a visitor with the same email exists in the project, they are automatically linked.

Request Body

email (required) — Customer email

name (optional) — Customer name

currency (optional) — Default currency (e.g. usd, eur). Defaults to usd.

externalId (optional) — Your system's customer ID

metadata (optional) — Arbitrary key-value pairs

bash
curl -X POST "https://app.himetrica.com/api/v1/revenue/customers" \
  -H "X-API-Key: hm_sk_your_secret_key" \
  -H "Content-Type: application/json" \
  -d '{
    "email": "jane@example.com",
    "name": "Jane Doe",
    "currency": "usd"
  }'

Response 201

json
{
  "id": "a1b2c3d4-...",
  "externalCustomerId": "manual_f8e2a1b3c4d5",
  "email": "jane@example.com",
  "name": "Jane Doe",
  "currency": "usd",
  "totalRevenue": 0,
  "totalRevenueUsd": 0,
  "activeSubscriptions": 0,
  "subscriptions": [],
  "createdAt": "2025-03-19T12:00:00.000Z",
  "updatedAt": "2025-03-19T12:00:00.000Z"
}

GET/revenue/customers

List manual customers with pagination and optional search.

Query Parameters

page — Page number (default: 1)

limit — Items per page (default: 50, max: 100)

search — Filter by email or name

bash
curl "https://app.himetrica.com/api/v1/revenue/customers?page=1&limit=20&search=jane" \
  -H "X-API-Key: hm_sk_your_secret_key"

GET/revenue/customers/:id

Get a single customer with their subscriptions and charge history.

bash
curl "https://app.himetrica.com/api/v1/revenue/customers/{id}" \
  -H "X-API-Key: hm_sk_your_secret_key"

PUT/revenue/customers/:id

Update a customer's fields. Only include the fields you want to change.

bash
curl -X PUT "https://app.himetrica.com/api/v1/revenue/customers/{id}" \
  -H "X-API-Key: hm_sk_your_secret_key" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Jane Smith",
    "metadata": { "plan": "enterprise" }
  }'

DELETE/revenue/customers/:id

Delete a customer and all their associated charges. This action is irreversible.

bash
curl -X DELETE "https://app.himetrica.com/api/v1/revenue/customers/{id}" \
  -H "X-API-Key: hm_sk_your_secret_key"

Charges

Charges represent one-time payments or invoice line items. They update the customer's total revenue and appear in the revenue timeline chart.

POST/revenue/customers/:id/charges

Add a charge to a customer. The customer's total revenue is automatically recalculated.

Request Body

amount (required) — Charge amount in the smallest currency unit (e.g. cents)

currency (optional) — ISO currency code. Defaults to the customer's currency.

status (optional)succeeded (default), pending, failed, refunded

description (optional) — Human-readable description

paymentMethod (optional) — e.g. card, bank_transfer, pix

cardBrand (optional) — e.g. visa, mastercard

chargeDate (optional) — ISO 8601 timestamp. Defaults to now.

externalId (optional) — Your system's charge/invoice ID. Used for idempotency.

bash
curl -X POST "https://app.himetrica.com/api/v1/revenue/customers/{id}/charges" \
  -H "X-API-Key: hm_sk_your_secret_key" \
  -H "Content-Type: application/json" \
  -d '{
    "amount": 9900,
    "currency": "usd",
    "description": "Pro Plan - March 2025",
    "externalId": "inv_abc123"
  }'

Response 201

json
{
  "externalId": "inv_abc123",
  "amount": 9900,
  "amountUsd": 9900,
  "currency": "usd",
  "status": "succeeded",
  "description": "Pro Plan - March 2025",
  "createdAt": "2025-03-19T12:00:00.000Z"
}

DELETE/revenue/charges/:id

Delete a charge. The customer's total revenue is automatically recalculated.

bash
curl -X DELETE "https://app.himetrica.com/api/v1/revenue/charges/{chargeId}" \
  -H "X-API-Key: hm_sk_your_secret_key"

Subscriptions

Subscriptions represent recurring revenue and are used to calculate MRR. They are stored on the customer object and update the MRR charts in your dashboard.

POST/revenue/customers/:id/subscriptions

Add a subscription to a customer.

Request Body

productName (required) — Name of the plan/product

amount (required) — Recurring amount per interval (in smallest currency unit)

currency (optional) — ISO currency code. Defaults to customer's currency.

interval (optional)month (default) or year

status (optional)active (default), trialing, canceled, past_due

externalId (optional) — Your system's subscription ID

currentPeriodStart / currentPeriodEnd (optional) — ISO 8601 dates

canceledAt (optional) — When the subscription was canceled

trialStart / trialEnd (optional) — Trial period dates

bash
curl -X POST "https://app.himetrica.com/api/v1/revenue/customers/{id}/subscriptions" \
  -H "X-API-Key: hm_sk_your_secret_key" \
  -H "Content-Type: application/json" \
  -d '{
    "productName": "Pro Plan",
    "amount": 2900,
    "currency": "usd",
    "interval": "month",
    "status": "active"
  }'

Response 201

json
{
  "externalId": "manual_sub_d4e5f6a7b8c9",
  "productName": "Pro Plan",
  "status": "active",
  "amount": 2900,
  "amountUsd": 2900,
  "currency": "usd",
  "interval": "month",
  "createdAt": "2025-03-19T12:00:00.000Z"
}

PUT/revenue/customers/:id/subscriptions/:subId

Update a subscription. Use this to change the plan, cancel, or update the status. The subId is the subscription's externalId.

bash
curl -X PUT "https://app.himetrica.com/api/v1/revenue/customers/{id}/subscriptions/{subId}" \
  -H "X-API-Key: hm_sk_your_secret_key" \
  -H "Content-Type: application/json" \
  -d '{
    "status": "canceled",
    "canceledAt": "2025-03-19T00:00:00.000Z"
  }'

DELETE/revenue/customers/:id/subscriptions/:subId

Remove a subscription entirely. If you want to mark it as canceled instead, use the PUT endpoint with status: "canceled".

bash
curl -X DELETE "https://app.himetrica.com/api/v1/revenue/customers/{id}/subscriptions/{subId}" \
  -H "X-API-Key: hm_sk_your_secret_key"

Idempotency

When you provide an externalId on a charge, the API uses a unique constraint to prevent duplicates. If a charge with the same externalId already exists, the existing record is updated instead of creating a duplicate.

This makes it safe to retry webhook deliveries or re-run import scripts without worrying about double-counting revenue.

Best practice

Always pass your payment provider's transaction ID as externalId when recording charges. If omitted, a random ID is generated and idempotency is not enforced.

Currency Conversion

All amounts are stored in the original currency and automatically converted to USD using live exchange rates (updated hourly). The USD equivalent is used for aggregated metrics like MRR, total revenue, and top customers.

Supported currencies include all major ISO 4217 codes (EUR, GBP, BRL, JPY, etc.).

Error Handling

All errors return a JSON object with an error field.

StatusMeaning
400Bad request — missing or invalid parameters
401Unauthorized — missing or invalid secret key
404Customer, charge, or subscription not found
429Rate limited — too many requests
500Internal server error
503Service temporarily unavailable

Examples

Node.js / TypeScript

typescript
const API_URL = "https://app.himetrica.com/api/v1";
const SECRET_KEY = process.env.HIMETRICA_SECRET_KEY; // hm_sk_...

// Create a customer
const customer = await fetch(`${API_URL}/revenue/customers`, {
  method: "POST",
  headers: {
    "X-API-Key": SECRET_KEY,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    email: "jane@example.com",
    name: "Jane Doe",
    currency: "usd",
  }),
}).then(r => r.json());

// Add a charge
await fetch(`${API_URL}/revenue/customers/${customer.id}/charges`, {
  method: "POST",
  headers: {
    "X-API-Key": SECRET_KEY,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    amount: 9900,
    currency: "usd",
    description: "Pro Plan - March 2025",
    externalId: "inv_abc123", // prevents duplicates
  }),
});

// Add a subscription
await fetch(`${API_URL}/revenue/customers/${customer.id}/subscriptions`, {
  method: "POST",
  headers: {
    "X-API-Key": SECRET_KEY,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    productName: "Pro Plan",
    amount: 2900,
    interval: "month",
  }),
});

Python

python
import requests
import os

API_URL = "https://app.himetrica.com/api/v1"
SECRET_KEY = os.environ["HIMETRICA_SECRET_KEY"]  # hm_sk_...
HEADERS = {"X-API-Key": SECRET_KEY, "Content-Type": "application/json"}

# Create a customer
customer = requests.post(
    f"{API_URL}/revenue/customers",
    headers=HEADERS,
    json={"email": "jane@example.com", "name": "Jane Doe", "currency": "usd"},
).json()

# Add a charge
requests.post(
    f"{API_URL}/revenue/customers/{customer['id']}/charges",
    headers=HEADERS,
    json={
        "amount": 9900,
        "currency": "usd",
        "description": "Pro Plan - March 2025",
        "externalId": "inv_abc123",
    },
)

# Add a subscription
requests.post(
    f"{API_URL}/revenue/customers/{customer['id']}/subscriptions",
    headers=HEADERS,
    json={"productName": "Pro Plan", "amount": 2900, "interval": "month"},
)

Webhook Handler (Express)

A common pattern is to use the Revenue API inside webhook handlers from your payment provider. This example shows how to record payments from a custom gateway:

typescript
// Example: Webhook handler for a custom payment provider
app.post("/webhooks/payments", async (req, res) => {
  const event = req.body;

  if (event.type === "payment.succeeded") {
    // Find or create customer in Himetrica
    let customer;
    try {
      const list = await fetch(
        `${API_URL}/revenue/customers?search=${encodeURIComponent(event.customer_email)}`,
        { headers: { "X-API-Key": SECRET_KEY } }
      ).then(r => r.json());

      customer = list.customers[0];
    } catch {}

    if (!customer) {
      customer = await fetch(`${API_URL}/revenue/customers`, {
        method: "POST",
        headers: { "X-API-Key": SECRET_KEY, "Content-Type": "application/json" },
        body: JSON.stringify({
          email: event.customer_email,
          name: event.customer_name,
          externalId: event.customer_id,
        }),
      }).then(r => r.json());
    }

    // Record the charge (externalId prevents duplicates on retries)
    await fetch(`${API_URL}/revenue/customers/${customer.id}/charges`, {
      method: "POST",
      headers: { "X-API-Key": SECRET_KEY, "Content-Type": "application/json" },
      body: JSON.stringify({
        amount: event.amount,
        currency: event.currency,
        externalId: event.payment_id,
        description: event.description,
        chargeDate: event.created_at,
      }),
    });
  }

  res.json({ received: true });
});
Himetrica - Analytics That Actually Matter