# Core concepts

> The objects behind every endpoint and tool: workspaces, members, contacts, conversations, activities, numbers, and the Astra agent.

Source: https://dev.ody.co/get-started/core-concepts

Every API call, MCP tool and webhook event talks about the same handful of objects. Learning them once makes the whole surface predictable.

## The object model

| Object | What it is | Where you meet it |
|---|---|---|
| **Workspace** | One business account: its members, numbers, contacts, inbox, plan and billing. Every key, request and event belongs to exactly one workspace. | Implicit — your key decides it |
| **Member** | A person in the workspace with a role (`owner`, `admin`, `member`). API keys act as the member who created them. | `assigneeUid`, `authorUid` |
| **Contact** | A person or company you talk to: names, labeled phones and emails, notes, tags and string properties. | [Contacts](https://dev.ody.co/api/contacts) |
| **Number** | A phone number the workspace owns (`local` or `toll_free`). Its `voiceMode` decides who answers: `app` (ring the team), `agent` (Astra), `voicemail`, or `forward`. | [Numbers](https://dev.ody.co/api/numbers) |
| **Conversation** | The thread between one of your numbers and an outside party. Status is `open` or `done`. | [Conversations](https://dev.ody.co/api/conversations) |
| **Activity** | One item on a conversation's timeline: `message`, `call`, `voicemail`, `internal_note`, or `system`. Messages carry a `deliveryStatus`; calls carry call metadata, transcript and AI tags. | Inside a conversation thread |
| **Agent (Astra)** | The workspace's AI receptionist: greeting, voice, tone, jobs, hours behaviour. Edited as a **draft**, then **published** to telephony. | [Agent](https://dev.ody.co/api/agent) |
| **Webhook endpoint** | An HTTPS URL subscribed to event types, with its own signing secret. | [Webhook endpoints](https://dev.ody.co/api/webhook-endpoints) |

## Identifiers and formats

- **IDs** are UUIDs (`3f2b…`). A malformed ID returns `404 not_found`, the same as an unknown one.
- **Phone numbers** are always [E.164](https://en.wikipedia.org/wiki/E.164): `+15125550123`. In URL paths, encode the `+` as `%2B`.
- **Timestamps** are ISO-8601 UTC strings: `2026-10-14T18:02:11.000Z`.
- **Money** in number search results is a decimal string in USD (`"5.00"`).

## Permissions: who is acting

An API key is not a robot account. It **acts as the member who created it**, inside that workspace only:

- Every request runs through the same row-level security and role checks as that member's clicks in the app.
- [Scopes](https://dev.ody.co/get-started/scopes) can only *narrow* what the key can do, never widen it.
- If that member loses access, so does the key.

## Your code changes the real account

There is no separate sandbox workspace. Anything you change through the API or MCP is the account your team sees:

- A number bought via `POST /v1/api/numbers` shows up in **Settings → Numbers** immediately and bills like an in-app purchase.
- An outbound AI call placed with `POST /v1/api/calls` lands on the contact's timeline in the shared inbox, with recording, transcript and summary.
- The agent your code publishes is the one on the **AI agent** screen.

This is deliberate ("Agent Phone"): an AI or a script can provision and run a phone line, and a human can take over in the app at any moment. To experiment without side effects, use a [test key](https://dev.ody.co/get-started/test-mode).

## Billing parity

API and MCP actions bill exactly like the app:

| Action | Cost |
|---|---|
| Extra local number | $5/month add-on (the plan includes one local number) |
| Toll-free number | $10/month add-on |
| 10DLC registration | $20 one-time setup fee per local number, charged at submission |
| AI call (inbound or outbound) | 100 credits for up to 10 minutes, +10 per extra started minute; 10 credits if it connects but ends under 15 seconds; unanswered calls are free |

## Versioning

The API is versioned by date. The current contract is published in the [OpenAPI spec](https://api.ody.co/v1/api/openapi.json); webhook payloads carry an `apiVersion` field. Changes are additive: new endpoints, fields, scopes and event types appear without breaking existing integrations. Treat unknown fields and event types as forward-compatible and ignore them.
