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

# Signals

> Define what you measure and bill on

# Signals (Metrics)

A **signal**, also called a **metric**, is what you measure and bill on. It's the unit of work you charge for. Examples:

* A chatbot agent might have a signal called `messages` (charge per message)
* A document analyzer might have `pages_processed` (charge per page)
* An image generator might have `images_generated` (charge per image)
* A video service might have `minutes_transcoded` (charge per minute)

> **You don't have to call this endpoint to create a signal.** When you fire a usage event with a `signalName` MarginFront has not seen before, the signal record is created automatically. Use this endpoint when you want to set the signal `type`, description, or other fields up front, or to update an existing signal.

You attach a signal to an agent and log usage events against it. MarginFront keeps count, applies your pricing rules, and rolls it up into an invoice.

> **Pick the signal name to match how you bill.** The signal name IS the unit your customer pays for. If you bill per page, name it `pages` and fire ONE event per report with `quantity: pageCount`. If you bill per report, name it `reports` and fire ONE event with `quantity: 1`. Same underlying work, different invoice line. See [Choosing your signal name and quantity](/concepts#choosing-your-signal-name-and-quantity) for the full guide with examples.

***

## Signal types: `usage` vs `volume`

Every signal has a `type` that controls how it's billed:

| Type         | Also known as | Bills based on                   | Example                                   |
| ------------ | ------------- | -------------------------------- | ----------------------------------------- |
| **`usage`**  | OUTCOME       | Results actually delivered       | Charge per successfully analyzed document |
| **`volume`** | ACTIVITY      | Attempts made, successful or not | Charge per API call regardless of outcome |

Pick `usage` when you want to align pricing with customer value (they only pay when they get something). Pick `volume` when you want to charge for the resources consumed regardless of result (they pay for every attempt because every attempt costs you money).

Most usage-based pricing for AI products uses `usage`. When in doubt, start there.

***

## Create a signal

**In plain English:** Define a new billable metric for an agent. You do this once per metric, up front — not every time the agent works.

**Method & URL:**

```
POST /v1/signals
```

**Required fields:**

* `name` *(string, max 255 chars)* — Human-readable name. Shows up in reports and on invoices. Examples: `"Messages Processed"`, `"Documents Analyzed"`, `"Images Generated"`.
* `agentId` *(UUID string)* — The internal ID of the agent this signal belongs to. Get it from the agent's create response. (Note: this is the agent's `id`, not its `agentCode`.)

**Optional fields:**

* `shortName` *(string, max 255 chars)* — A machine-friendly version of the name. Used as the key in some API responses. Example: `"messages"`, `"docs_analyzed"`. If you don't provide one, it's derived from `name`.
* `type` *(`"usage"` | `"volume"`, default `"usage"`)* — See the explanation above.

**Example curl call:**

```bash theme={null}
curl -X POST https://api.marginfront.com/v1/signals \
  -H "x-api-key: mf_sk_test_..." \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Messages Processed",
    "shortName": "messages",
    "agentId": "b47e12fa-abcd-4567-8901-234567890abc",
    "type": "usage"
  }'
```

**What you get back (`201 Created`):**

```json theme={null}
{
  "id": "f12d34ab-cdef-0123-4567-89abcdef0123",
  "name": "Messages Processed",
  "shortName": "messages",
  "type": "usage",
  "agentId": "b47e12fa-abcd-4567-8901-234567890abc",
  "createdAt": "2026-04-10T14:30:00.000Z"
}
```

**Common errors:**

* **`400 Bad Request`** — Missing `name` or `agentId`, or `agentId` isn't a valid UUID format, or `type` isn't `"usage"` or `"volume"`.
* **`404 Not Found`** — The `agentId` you provided doesn't match any agent in this org.
* **`409 Conflict`** — A signal with that name or shortName already exists for this agent.

**When to call this:** Once, during setup, for each metric you want to bill on. Don't call this every time a usage event happens — that's what the usage/record endpoint is for.

***

## Read a signal

**Method & URL:**

```
GET /v1/signals/{signalId}
```

***

## List signals

**Method & URL:**

```
GET /v1/signals
```

**Useful query parameters** (check the controller for the full list):

* `agentId` — filter to signals for a specific agent

***

## Update a signal

**Method & URL:**

```
PATCH /v1/signals/{signalId}
```

**Fields:** Any field from create except `agentId` (you can't move a signal to a different agent — create a new one instead).

> **Careful with renaming.** If you change a signal's `shortName` after you've started logging usage events against it, any reports that grouped by shortName will split across the old and new names. Rename sparingly.

***

## Delete a signal

**Method & URL:**

```
DELETE /v1/signals/{signalId}
```

**What it does:** Permanently removes the signal. All historical usage events tied to this signal become orphaned — they still exist, but they'll stop showing up in most reports.

> **Usually you don't want to delete.** For ongoing operations, prefer to just stop logging events against an old signal. Deletion is mostly for cleaning up test data or correcting mistakes during setup.

***

## Bulk create signals

**Method & URL:**

```
POST /v1/signals/bulk
```

**When to use it:** If you're setting up many signals at once (e.g., spinning up a new agent with a dozen metrics), the bulk endpoint is faster than calling create in a loop. Check the controller for the exact request shape.

***

## Using the Node SDK

Signals aren't exposed as a separate resource in the `@marginfront/sdk` npm package. The SDK references signals by name (`signalName`) when you log usage events:

```js theme={null}
await mf.usage.record({
  customerExternalId: "acme-001",
  agentCode: "cs-bot-v2",
  signalName: "messages", // ← matches the signal's shortName
  model: "gpt-4o",
  inputTokens: 523,
  outputTokens: 117,
});
```

**To create signals, use the HTTP API above.** If you log a usage event with a `signalName` MarginFront has never seen, it auto-creates a minimal signal for you — convenient for prototyping, but you'll want to go back and set the right `name` and `type` afterwards.

***

## How signals fit into the whole billing chain

```
Customer  ─┐
           │
Agent ─────┼──── Signal ──── Pricing plan ──── Subscription
           │     (metric)    (how to price)    (per customer)
           │
Usage event (logged per signal, per customer)
```

* **Customer:** who's being billed
* **Agent:** what's doing the work
* **Signal:** what you're measuring
* **Pricing plan:** how much each unit of that signal costs
* **Subscription:** ties a customer to a pricing plan
* **Usage event:** a single measurement (customer X used signal Y, quantity Z)

When an invoice runs, MarginFront totals the usage events for each customer, applies the pricing plan from their subscription, and produces line items per signal. That's the whole pipeline.
