# Load records and files

Use append for new JSON records, upload for files, and upsert for rows that should update existing primary keys. You need a writable catalog and [lakehouse credentials](/docs/administration/lakehouse-credentials.md).

The steps below use the CLI. For application code, use the SDK or HTTP examples in step 2. Exact limits and request fields live in the [HTTP API reference](/docs/api-references/lakehouse.md).

## Set up with your agent

```text
Help me load records or files into Altertable. Read https://altertable.ai/docs/lakehouse/ingest/http-api.md first.

Inspect the source format and any existing ingestion code. Confirm the destination environment, catalog, schema, and table. Choose append for new JSON records, upload for files, or upsert for updates with a stable primary key. Ask only for information you cannot inspect.

Use the CLI for a one-off load or a supported SDK for application code. Keep lakehouse credentials in local or server-side secret configuration. Explain the chosen operation and obtain confirmation before writing data; do not overwrite an existing table.

Load a small representative sample, wait for insertion to finish, and query the destination. Compare the returned fields and values with the sample and inspect rejected or quarantined records. An accepted request is not sufficient verification. Summarize the result and show how to repeat the load.
```

> **Before using the Altertable CLI:**
>
> Install the CLI if you haven’t already:
>
> ```bash
> curl -fsSL https://install.altertable.ai | sh
> altertable login
> ```
>
> If `altertable` isn’t found after installation, open a new terminal. Run `altertable login` to log in to your organization and environment. Follow the [CLI setup guide](/docs/lakehouse/clients/cli.md#2-log-in) to configure credentials and verify access.

## Steps

### 1. Choose an ingest mode

| Operation | Use it for                                                            |
| --------- | --------------------------------------------------------------------- |
| Append    | Add new JSON records, with automatic table creation and new columns   |
| Upload    | Create a table from a CSV, JSON, or Parquet file, or append file rows |
| Upsert    | Update matching primary keys and insert new rows from a file          |

Use [bucket synchronization](/docs/lakehouse/ingest/object-storage.md) for scheduled file imports from object storage.

### 2. Append JSON records

From the CLI, append a test record to a new table. Replace `my_catalog` with your destination catalog:

```altertable
altertable append '{"event":"onboarding_test","source":"cli"}' --to my_catalog.main.events
```

Append acknowledges accepted records before insertion finishes. Allow a few seconds, then query the table to verify that they arrived. An acknowledgement alone does not verify insertion.

If a row fails insertion after retries, Altertable stores it in `altertable.main.quarantined_appends`. Inspect the error and original payload before correcting and resending it.

**Append from application code**

Use your [lakehouse credentials](/docs/administration/lakehouse-credentials.md#verification) to authenticate HTTP requests or initialize an SDK:

### cURL

```bash
curl -X POST "https://api.altertable.ai/append?catalog=my_catalog&schema=main&table=events" \
  -H "Authorization: Basic $ALTERTABLE_BASIC_AUTH_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"timestamp":"2026-01-01","event":"click","value":42}'
```

### JavaScript

```typescript
import { AltertableLakehouseClient } from '@altertable/lakehouse';

const client = new AltertableLakehouseClient({
  username: '{{LAKEHOUSE_USERNAME}}',
  password: '{{LAKEHOUSE_PASSWORD}}',
});

await client.append({
  catalog: 'my_catalog',
  schema: 'main',
  table: 'events',
  body: {
    Single: { timestamp: '2026-01-01', event: 'click', value: 42 },
  },
});
```

### Python

```python
from altertable_lakehouse import Client

client = Client(
    username="{{LAKEHOUSE_USERNAME}}",
    password="{{LAKEHOUSE_PASSWORD}}",
)

client.append(
    catalog="my_catalog",
    schema="main",
    table="events",
    data={"timestamp": "2026-01-01", "event": "click", "value": 42},
)
```

### Ruby

```ruby
require 'altertable/lakehouse'

client = Altertable::Lakehouse::Client.new(
  username: '{{LAKEHOUSE_USERNAME}}',
  password: '{{LAKEHOUSE_PASSWORD}}'
)

client.append(
  catalog: 'my_catalog',
  schema: 'main',
  table: 'events',
  payload: { timestamp: '2026-01-01', event: 'click', value: 42 }
)
```

### Go

```go
import (
	"context"
	"log"

	altertable "github.com/altertable-ai/altertable-lakehouse-go"
)

client, err := altertable.NewClient(altertable.Config{
	Username: "{{LAKEHOUSE_USERNAME}}",
	Password: "{{LAKEHOUSE_PASSWORD}}",
})
if err != nil {
	log.Fatal(err)
}

_, err = client.Append(context.Background(), altertable.AppendParams{
	Catalog: "my_catalog",
	Schema:  "main",
	Table:   "events",
}, altertable.AppendRequest{
	Single: altertable.AppendPayload{
		"timestamp": "2026-01-01",
		"event":     "click",
		"value":     42,
	},
})
if err != nil {
	log.Fatal(err)
}
```

### PHP

```php
<?php

use Altertable\Lakehouse\Config\LakehouseConfig;
use Altertable\Lakehouse\LakehouseClient;
use Altertable\Lakehouse\Models\AppendPayload;
use Altertable\Lakehouse\Models\AppendRequest;

$config = LakehouseConfig::builder()
    ->withCredentials('{{LAKEHOUSE_USERNAME}}', '{{LAKEHOUSE_PASSWORD}}')
    ->build();

$client = new LakehouseClient($config);

$client->append(
    'my_catalog',
    'main',
    'events',
    AppendRequest::single(new AppendPayload(
        data: [[
            'timestamp' => '2026-01-01',
            'event' => 'click',
            'value' => 42,
        ]],
        columns: ['timestamp', 'event', 'value'],
    )),
);
```

### Rust

```rust
use altertable_lakehouse::{AltertableClient, AppendRequest};
use serde_json::json;
use std::collections::HashMap;

let client = AltertableClient::builder()
    .credentials("{{LAKEHOUSE_USERNAME}}", "{{LAKEHOUSE_PASSWORD}}")
    .build()?;

let payload = HashMap::from([
    ("timestamp".to_string(), json!("2026-01-01")),
    ("event".to_string(), json!("click")),
    ("value".to_string(), json!(42)),
]);

client
    .append(
        "my_catalog",
        "main",
        "events",
        None,
        &AppendRequest::Single(payload),
    )
    .await?;
```

### 3. Upload files

To create a new table from a file:

```altertable
altertable upload users.csv --to my_catalog.main.users --mode create
```

To add file rows to an existing table, use `--mode append`. Use `--mode overwrite` only when you intend to replace the table with the uploaded data. See [upload behavior](/docs/api-references/lakehouse.md#post-upload) for supported formats and limits.

### 4. Upsert by primary key

If `users.csv` contains a stable `id` column, update matching rows and insert new ones with:

```altertable
altertable upsert users.csv --to my_catalog.main.users --key id
```

Check that the key is populated and identifies the rows you intend to update. See [upsert behavior](/docs/api-references/lakehouse.md#post-upsert) for HTTP requests and supported formats.

## Verification

After uploading or upserting the file, inspect the destination:

```altertable
altertable query "SELECT * FROM my_catalog.main.users LIMIT 10"
```

Compare the returned values with the file, including any rows you expected to update. For append, query its destination table after processing has completed.

## Troubleshooting

- **Unauthorized request:** check the destination environment and lakehouse credentials.
- **A new append row is missing:** wait for processing, then inspect `altertable.main.quarantined_appends` for failures.
- **The file exceeds the request limit:** consult the [endpoint limits](/docs/api-references/lakehouse.md#post-upload) and split the data when needed.
- **Upsert changes unexpected rows:** check the primary key column and its values before sending another request.