# Authentication

Use a CalmLens SDK on your server. Configure it with a project API key and project ID; the SDK sends the authentication header for you. Keep direct HTTP calls for integrations that cannot use an SDK; see the [API reference](/docs/api).

**JavaScript**

```javascript
const page = await client.assets.list({ pageIndex: 0 });
```

**python**

```python
import os
import json
from calmlens import CalmLensClient

client = CalmLensClient(
    api_key=os.environ["CALMLENS_API_KEY"],
    project_id="your-project-id",
)

options = json.loads("{\"pageIndex\":0}")
response = client.assets.list(options)
print(response)
```

**go**

```go
package main

import (
    "context"
    "encoding/json"
    "fmt"
    "log"
    "os"

    calmlens "github.com/Censkh/CalmLens/packages/go-sdk"
)

func main() {
    client, err := calmlens.NewClient(calmlens.Options{
        APIKey: os.Getenv("CALMLENS_API_KEY"),
        ProjectID: "your-project-id",
    })
    if err != nil { log.Fatal(err) }

    var options calmlens.ListAssetsOptions
    if err := json.Unmarshal([]byte("{\"pageIndex\":0}"), &options); err != nil { log.Fatal(err) }
    response, err := client.Assets.List(context.Background(), options)
    if err != nil { log.Fatal(err) }
    fmt.Println(response)
}
```

**ruby**

```ruby
require 'calmlens'

client = CalmLens::Client.new(api_key: ENV.fetch('CALMLENS_API_KEY'), project_id: ENV.fetch('CALMLENS_PROJECT_ID'))
result = client.assets.list(page_index: 0)
puts result
client.close
```

**rust**

```rust
use calmlens::{models::*, Client};

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let client = Client::new(std::env::var("CALMLENS_API_KEY")?, std::env::var("CALMLENS_PROJECT_ID")?)?;
    let result = client.assets().list(ListAssets { page_index: Some(0), ..Default::default() }).await?;
    println!("{result:?}");
    Ok(())
}
```

**java**

```java
import com.calmlens.CalmLensClient;
import com.calmlens.Models.*;

public class Example {
  public static void main(String[] args) {
    var client = new CalmLensClient(System.getenv("CALMLENS_API_KEY"), System.getenv("CALMLENS_PROJECT_ID"));
    var result = client.assets.list(new ListAssets().pageIndex(0));
    System.out.println(result);
  }
}
```

**curl**

```sh
curl -X GET "https://api.calmlens.com/v1/projects/$CALMLENS_PROJECT_ID/assets/page?pageIndex=0" \
  -H "X-Api-Key: $CALMLENS_API_KEY"
```

## Create and protect keys

Create keys under [API keys](/portal/api-keys). The complete key is shown only when it is created.

- Keep keys on a server or in a secret manager, never in browser bundles or source control.
- Give each integration its own named key so it can be revoked independently.
- Treat a revoked or expired key as unusable and rotate clients before expiration.
- A key is project-scoped. The project in the URL must match the key's project.

## Rotate a key

1. Create a replacement with the permissions your integration needs.
2. Update the secret in your server environment and deploy it.
3. Verify a request succeeds with the replacement, then revoke the old key.

## Choose permissions

| Integration needs | Grant |
| --- | --- |
| Submit content | Upload assets in the relevant project or folders. |
| Read reports | View assets in the relevant scope. |
| Fetch private files | Download assets. |
| Give browsers private access | Permission to create signed sharing URLs. |

Use [Members and permissions](/docs/guides/project-collaboration#folder-scoped-access) for custom folder scopes.

## CDN access

Public assets need no authentication. For private assets, prefer a short-lived [signed URL](/docs/guides/delivering-assets#signed-urls) in browsers and other untrusted clients.

Enable **Download assets** (`assets:download`) when creating or editing an API key to allow direct private CDN reads within its project. Keys without this permission need a short-lived signed URL or an authenticated owner session.

## Authentication failures

API errors use an HTTP status and a JSON body containing a stable `code`, a human-readable `message`, and usually a `traceId`. Log the trace ID when contacting support, but branch application logic on `code` rather than message text.

| Error | What to do |
| --- | --- |
| [`401 auth/missing-token`](/docs/api/assets#post-asset-error-auth-missing-token) | Send your project API key in the `X-Api-Key` header. |
| [`401 auth/invalid-api-key`](/docs/api/assets#post-asset-error-auth-invalid-api-key) | The key is unknown, revoked or expired. Check for typos or whitespace, or create a new key under [API keys](/portal/api-keys). |
| [`403 auth/project-mismatch`](/docs/api/assets#post-asset-error-auth-project-mismatch) | The key belongs to a different project. Use a key created for the project in the URL, or the project that owns the asset or folder. |

Do not retry `401` or `403` responses with the same key; fix the configuration first.
