# Getting started (https://dodomain.io/docs/getting-started)

From signup to a verified custom domain: create an app, mint a connect session, and receive the signed webhook.

## 1. Sign in and create an app [#1-sign-in-and-create-an-app]

Create your account at [app.dodomain.io/signin?mode=signup](https://app.dodomain.io/signin?mode=signup), with Google or with email and password. Every account gets a personal team, and the free plan needs no card.

Create an app (the `create_app` tool, or the app page). You get two keys:

- **Public key** (`dd_pk_...`): identifies the app in the widget; safe to expose, grants nothing.
- **Secret key** (`dd_sk_...`): authenticates your server's API calls. It is shown once and stored only as a hash; keep it server-side, never in a browser bundle. You can rotate it with the `rotate_api_key` tool if it leaks.

## 2. Mint a connect session [#2-mint-a-connect-session]

<DocsIllustration name="records" />

One call from your server. Describe the records your product needs on the user's domain: `host` is relative to the domain (`"app"` becomes `app.customer.com`; use `"@"` for the apex), and `value` is where the record points.

```bash
curl -X POST https://app.dodomain.io/api/v1/sessions \
  -H "Authorization: Bearer dd_sk_YOUR_SECRET_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "domain": "customer.com",
    "records": [
      { "type": "CNAME", "host": "app", "value": "edge.yourproduct.com" },
      { "type": "TXT", "host": "_yourproduct", "value": "yourproduct-verify=7f3a9c" }
    ],
    "returnUrl": "https://yourproduct.com/settings/domains"
  }'
```

The response is a short-lived session (24-hour expiry):

```json
{
  "id": "cmcwl0d9x0001…",
  "token": "dd_sess_Qy3v…",
  "expiresAt": "2026-07-12T14:03:07.000Z",
  "connectUrl": "https://app.dodomain.io/connect/dd_sess_Qy3v…"
}
```

## 3. Send your user to the connect flow [#3-send-your-user-to-the-connect-flow]

Open `connectUrl` (link, redirect, or new tab). The hosted page detects the user's DNS provider: on Cloudflare it offers one-click apply via OAuth; everywhere else it shows provider-specific copy-paste instructions with live per-record verification. Prefer it embedded? The `@dodomain/connect` widget opens the same flow in a modal on your own page.

## 4. Receive the signed webhook [#4-receive-the-signed-webhook]

Add a webhook endpoint for your app with the `add_webhook_endpoint` tool or `POST /api/v1/webhook-endpoints`; you get a `whsec_...` signing secret. The moment the records verify against authoritative DNS, DoDomain POSTs a `connection.verified` event to it. Verify the signature (header `x-dodomain-signature`, format `t=<unix ms>,v1=<hex HMAC-SHA256>`), then activate the domain in your product.

<Callout title="Or use a server SDK">
  [`@dodomain/node`](https://dodomain.io/docs/node-sdk) and [`dodomain-sdk`](https://dodomain.io/docs/python-sdk) wrap all of this: a
  schema-validated `sessions.create` plus a webhook verifier, over the same shapes. The raw HTTP
  above stays fully supported; the SDKs are a convenience, not a requirement.
</Callout>

```ts
import { DoDomain, verifyWebhook } from "@dodomain/node";

const dodomain = new DoDomain({ secretKey: process.env.DODOMAIN_SECRET_KEY });
const session = await dodomain.sessions.create({
  domain: "customer.com",
  records: [{ type: "CNAME", host: "app", value: "edge.yourproduct.com" }],
});
// session.connectUrl → hand to your user
// verifyWebhook(secret, rawBody, signatureHeader) → boolean
```

The same two steps in Python:

```python
import os
from dodomain import DoDomain, DnsRecord, verify_webhook

client = DoDomain(secret_key=os.environ["DODOMAIN_SECRET_KEY"])
session = client.sessions.create(
    domain="customer.com",
    records=[DnsRecord(type="CNAME", host="app", value="edge.yourproduct.com")],
)
# session.connect_url → hand to your user
# verify_webhook(secret, raw_body, signature_header) → bool
```
