---
title: "Quickstart"
description: "From a sandbox key to your first contact in four steps: create the key, ask who you are, create a contact, look it up in Bodo."
lang: en
url: https://developers.bodo-app.com/en/quickstart/
apiVersion: 2026-11-01
---

# Quickstart

From a sandbox key to your first contact in four steps: create the key, ask who you are, create a contact, look it up in Bodo.

## Create a sandbox key

In Bodo: Settings › Interfaces › API keys › “Create key”.

| Step in the wizard | Value for the quickstart |
| --- | --- |
| Environment | `test`, binds to “Musterfirma GmbH (Sandbox)” |
| Scope | Contacts: read & write · Companies: read · Rest: no access |
| IP binding | your current IP, e.g. `198.51.100.7/32` (required for writes) |
| Expiry | 30 days (default) |

> **The API is already on in the sandbox** Your organization’s sandbox already has `apiAccess` switched on, with test data only. So the quickstart needs no approval from an admin. The one precondition: your organization has declared in Bodo whether professional secrecy applies to it; without that declaration the API stays off in the sandbox as well. For a live key (`live`) an org admin first has to enable the API of your organization under Settings › Interfaces › API; until then the button is missing there.

The secret is shown only once. Keep it in an environment variable:

**Shell**

```bash
export BODO_API_KEY="bodo_uk_test_…"
```

## Who am I?

`GET /v1/me` names the principal, the organization and your own key with expiry, pin and effective scopes.

**Request**

```bash
curl https://api.bodo-app.com/v1/me \
  -H "Authorization: Bearer $BODO_API_KEY"
```

**Response**

```http
HTTP/1.1 200 OK
Bodo-Version: 2026-11-01
Request-Id: req_01JB6…

{
  "authMethod": "apiKey",
  "principalType": "user",
  "principalId": "usr_…",
  "principalName": "Max Mustermann",
  "organization": {
    "id": "org_…",
    "name": "Musterfirma GmbH (Sandbox)"
  },
  "key": {
    "kidShort": "…Kx9a",
    "environment": "test",
    "versionPin": "2026-11-01",
    "expiresAt": "2026-12-03T09:00:00Z"
  },
  "grant": null,
  "scopes": [
    "contacts:read",
    "contacts:write",
    "companies:read"
  ],
  "openFamilies": [],
  "apiVersion": "2026-11-01"
}
```

## Create a contact

Writes start in the preview channel and therefore need `Bodo-Preview: true`. With the first follow-up version, writes for `contacts`, `companies` and `tasks` move to stable and the header is no longer needed. `Idempotency-Key` is required for POST; with the same key a retry is safe.

**curl**

```bash
curl -X POST https://api.bodo-app.com/v1/contacts \
  -H "Authorization: Bearer $BODO_API_KEY" \
  -H "Bodo-Preview: true" \
  -H "Idempotency-Key: $(uuidgen)" \
  -H "Content-Type: application/json" \
  -d '{"firstName":"Erika","lastName":"Mustermann","email":"erika@musterfirma.de"}'
```

Or with the [SDK](/en/sdks/), which sets the preview header and the idempotency key itself:

**Create a contact with the SDK**

TypeScript · SDK:

```typescript
// Portal D2 · Quickstart — steps 2 and 3 with the SDK: who am I, then one contact.
// Run: BODO_API_KEY=bodo_uk_test_… bun examples/quickstart.ts
import { Bodo, BodoApiError } from "@bodo/api";

const bodo = new Bodo({
  apiKey: process.env.BODO_API_KEY,
  preview: true, // writes are in the preview channel: sends Bodo-Preview: true
});

// Step 2 · Who am I?
const me = await bodo.me();
console.log(me.principalType, me.authMethod);

// Step 3 · Create a contact. The SDK sets the Idempotency-Key itself, retries included.
try {
  const contact = await bodo.contacts.create({
    firstName: "Erika",
    lastName: "Mustermann",
    email: "erika@musterfirma.de",
  });
  console.log(contact.id, contact.displayName);
} catch (err) {
  if (err instanceof BodoApiError) {
    console.error(err.code, err.requestId); // the stable code, never the text
  }
  throw err;
}
```

Python · SDK:

```python
# Steps 2 and 3 with the SDK: who am I, then one contact.
# Run: BODO_API_KEY=bodo_uk_test_… python examples/quickstart.py
import os

from bodo_api import Bodo, BodoApiError

bodo = Bodo(
    api_key=os.environ["BODO_API_KEY"],
    preview=True,  # writes are in the preview channel: sends Bodo-Preview: true
)

# Step 2 · Who am I?
me = bodo.me()
print(me.principal_type, me.auth_method)

# Step 3 · Create a contact. The SDK sets the Idempotency-Key itself, retries included.
try:
    contact = bodo.contacts.create(
        first_name="Erika",
        last_name="Mustermann",
        email="erika@musterfirma.de",
    )
    print(contact.id, contact.display_name)
except BodoApiError as err:
    print(err.code, err.request_id)  # the stable code, never the text
    raise
```

201 Created · `Location: /v1/contacts/con_…` · `ETag`

## Look it up in Bodo

The contact is in the sandbox. Its timeline shows the plug mark, so the origin is visible.

**Max Mustermann** created the contact Erika Mustermann, via the API.

[Continue to the reference](/en/reference/)
