---
title: "From credentials to your first signature"
description: "From operator-issued credentials to your first signature, in one pass."
---

> Documentation Index
> Fetch the complete documentation index at: https://tee.hypetrade.xyz/llms.txt
> Use this file to discover all available pages before exploring further.

# From credentials to your first signature

This walks the shortest honest path to one working result: a signature produced
by a key the service manages for you. It assumes your operator has already
issued you a tenant API key and secret.

## What you need

Set these once for the session. The API key and secret come from your operator —
they are not self-service.

```bash
export API_URL=https://tee-api.hypetrade.xyz/v1
export API_KEY=your-api-key
export API_SECRET=your-api-secret
```

> **Treat these like passwords**
>
> Tenant credentials administer every workspace you own. Keep them out of client
> code, browsers, and source control. See [Safe usage](/safe-usage/).

1. **Step 1**

### Create a workspace

   A workspace is a password-protected container. Pick a slug and a strong
   password; you will need the password again to open it.

```bash
curl -X POST "$API_URL/workspaces" \
  -H "X-Api-Key: $API_KEY" \
  -H "X-Api-Secret: $API_SECRET" \
  -H "content-type: application/json" \
  -d '{"slug":"demo","password":"a-strong-workspace-password"}'
```

   Slugs are lowercase letters, digits, and hyphens, must start alphanumeric, and
   run to 63 characters. A `201` returns the workspace record.

2. **Step 2**

### Mint a workspace token

   This is the unlock. The token that comes back holds the workspace open, and its
   lifetime *is* the session.

```bash
curl -X POST "$API_URL/auth/token" \
  -H "X-Api-Key: $API_KEY" \
  -H "X-Api-Secret: $API_SECRET" \
  -H "content-type: application/json" \
  -d '{"workspace":"demo","password":"a-strong-workspace-password"}'
```

```json
{
  "token": "eyJhbGciOi...",
  "expiresAt": "2026-08-18T12:34:56.000Z",
  "workspace": "demo",
  "scopes": ["read", "write", "sign"]
}
```

   Capture the token — every remaining step uses it:

```bash
export TOKEN=the-token-value
```

3. **Step 3**

### Create an account

   An HD account generates its own mnemonic. The mnemonic is **never** returned in
   a response body; `generatedSecret: true` only tells you one was created.

```bash
curl -X POST "$API_URL/accounts" \
  -H "Authorization: Bearer $TOKEN" \
  -H "content-type: application/json" \
  -d '{"displayName":"Treasury","kind":"HD"}'
```

   The account slug is generated by the service, not chosen by you. It comes back
   at `account.slug`:

```json
{
  "account": {
"slug": "treasury-a1b2",
"displayName": "Treasury",
"kind": "HD",
"locked": false,
"defaultNetwork": "ethereum",
"wallets": []
  }
}
```

```bash
export ACCOUNT=treasury-a1b2
```

   > **The account closes on a timer**
   >
   > Creating an account opens it, and that opening lasts `ttl.accountAbsoluteSec` —
   > **five minutes by default**. If you take longer than that over the remaining
   > steps, the next call answers `423` with `account_locked`. That is not an error
   > in what you typed; it is the custody window closing.
   >
   > Reopen it and carry on — for an account that shares the workspace password,
   > the workspace password is the one to send:
   >
   > ```bash
curl -X POST "$API_URL/accounts/$ACCOUNT/unlock" \
  -H "Authorization: Bearer $TOKEN" \
  -H "content-type: application/json" \
  -d '{"accountPassword":"'"$PASSWORD"'"}'
```

4. **Step 4**

### Derive wallets

   A new account starts empty. Derive wallets to get addresses.

```bash
curl -X POST "$API_URL/accounts/$ACCOUNT/wallets" \
  -H "Authorization: Bearer $TOKEN" \
  -H "content-type: application/json" \
  -d '{"count":2}'
```

```json
{ "before": 0, "after": 2 }
```

5. **Step 5**

### Read the addresses

```bash
curl "$API_URL/accounts/$ACCOUNT/wallets" \
  -H "Authorization: Bearer $TOKEN"
```

   Each wallet carries its addresses, one per VM. Each is fixed to the network
   the account was created with — see [Accounts, wallets, and addresses](/concepts/accounts-wallets-addresses/):

```json
{
  "wallets": [
{
  "id": 1,
  "tags": [],
  "addresses": [
    { "publicKey": "0xabc…", "vm": "evm", "network": "ethereum", "chainId": 1 }
  ]
}
  ]
}
```

6. **Step 6**

### Sign a message

   Signing addresses a public key directly — you never walk account → wallet →
   address to sign. Resolution is scoped to your own workspace.

```bash
curl -X POST "$API_URL/sign/message" \
  -H "Authorization: Bearer $TOKEN" \
  -H "content-type: application/json" \
  -d '{"address":"0xabc…","message":"hello from tee-docker"}'
```

```json
{ "address": "0xabc…", "signature": "0x…" }
```

7. **Step 7**

### Lock up when you are done

   Revoking ends this token's lease. It is the only token you minted here, so the
   workspace locks with it.

```bash
curl -X DELETE "$API_URL/auth/token" \
  -H "Authorization: Bearer $TOKEN"
```

## What just happened

You used both credential tiers. Steps 1 and 2 used **tenant credentials** —
the only two calls that do. Everything after used the **workspace token**, which
carried the default `read`, `write`, and `sign` scopes.

## Next

- [Workspaces and tokens](/concepts/workspaces-and-tokens/) — session lifetime, refresh, and what expiry looks like
- [Scopes and permissions](/concepts/scopes/) — including why `export` is different
- [Send transactions](/guides/send-transactions/) — build, simulate, submit
- [Errors](/errors/) — the full code catalog and what to do about each

Source: https://tee.hypetrade.xyz/get-started/index.mdx
