# Code Examples

Use an API key from **Settings → API Keys**. Replace `{workspace}` with your workspace name and `{api_key}` with the key shown when you create it.

## Laravel

```php
use Illuminate\Support\Facades\Http;

$response = Http::withToken('{api_key}')
    ->acceptJson()
    ->get('https://app.example.com/api/v1/{workspace}/entries', [
        'contentBucket' => 'content',
        'contentType' => 'pages',
    ]);

$entries = $response->throw()->json();
```

## PHP

```php
$url = 'https://app.example.com/api/v1/{workspace}/entries?contentBucket=content&contentType=pages';

$ch = curl_init($url);
curl_setopt_array($ch, [
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_HTTPHEADER => [
        'Accept: application/json',
        'Authorization: Bearer {api_key}',
    ],
]);

$body = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_RESPONSE_CODE);
curl_close($ch);

if ($status >= 400) {
    throw new RuntimeException($body);
}

$entries = json_decode($body, true, flags: JSON_THROW_ON_ERROR);
```

## Node.js

```js
const url = new URL("https://app.example.com/api/v1/{workspace}/entries");
url.searchParams.set("contentBucket", "content");
url.searchParams.set("contentType", "pages");

const response = await fetch(url, {
  headers: {
    Accept: "application/json",
    Authorization: "Bearer {api_key}",
  },
});

if (!response.ok) {
  throw new Error(await response.text());
}

const entries = await response.json();
```

## Create an entry

```js
const response = await fetch("https://app.example.com/api/v1/{workspace}/entries?contentBucket=content&contentType=pages", {
  method: "POST",
  headers: {
    Accept: "application/json",
    Authorization: "Bearer {api_key}",
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    status: "published",
    fields: {
      title: "About",
      slug: "about",
    },
  }),
});

const entry = await response.json();
```

## Upload a file

Uploads require the `files:write` ability and the key creator's permission to create workspace files. Send one `file` per request. The response is the file object directly, with an integer `id`, metadata, permanent `download_url`, and a `temporary_url` valid for 10 minutes. `temporary_url_expires_at` gives its expiry. Use `GET /api/v1/{workspace}/files/{id}` with `files:read` to obtain a fresh temporary link.

### curl

```shell
curl "https://app.example.com/api/v1/acme/files" \
  -H "Authorization: Bearer $VIRESSO_API_KEY" \
  -H "Accept: application/json" \
  -F "file=@./photo.png"
```

### Laravel upload

```php
use Illuminate\Support\Facades\Http;

$stream = fopen(storage_path('app/photo.png'), 'rb');

try {
    $file = Http::withToken('{api_key}')
        ->acceptJson()
        ->attach('file', $stream, 'photo.png')
        ->post('https://app.example.com/api/v1/{workspace}/files')
        ->throw()
        ->json();
} finally {
    if (is_resource($stream)) {
        fclose($stream);
    }
}

$fileId = $file['id'];
```

### Node.js upload and attachment

This server-side example uses Node.js 20+ native `fetch`, `FormData`, and `openAsBlob`. The key needs both `files:write` and `entries:write`. It assumes the `pages` content type has `title`, `slug`, and a single attachment field named `hero_image`.

```js
import { openAsBlob } from "node:fs";

const baseUrl = "https://app.example.com/api/v1/acme";
const headers = {
  Accept: "application/json",
  Authorization: `Bearer ${process.env.VIRESSO_API_KEY}`,
};

const body = new FormData();
body.set("file", await openAsBlob("./photo.png"), "photo.png");

const upload = await fetch(`${baseUrl}/files`, {
  method: "POST",
  headers,
  body,
});
if (!upload.ok) throw new Error(await upload.text());
const file = await upload.json();

const create = await fetch(`${baseUrl}/entries?contentBucket=content&contentType=pages`, {
  method: "POST",
  headers: { ...headers, "Content-Type": "application/json" },
  body: JSON.stringify({
    status: "draft",
    fields: {
      title: "About",
      slug: "about",
      hero_image: file.id,
    },
  }),
});
if (!create.ok) throw new Error(await create.text());
const entry = await create.json();
```

Let the HTTP client set the multipart boundary; do not add a JSON `Content-Type` to the upload request. Upload and entry creation are separate requests: if entry validation fails, the file remains available in the workspace library.

See [Files API](/docs/api/files) for permitted types, size limits, base64 uploads, and downloads.

## Delete a file

Deletion requires `files:delete` and the key creator's permission to delete workspace files. Use the integer file ID returned by an upload or an entry attachment. The file is also removed from attachment fields in the workspace's entries.

```shell
curl -X DELETE "https://app.example.com/api/v1/acme/files/18" \
  -H "Authorization: Bearer $VIRESSO_API_KEY" \
  -H "Accept: application/json"
```

For a server-side Node.js client:

```js
const response = await fetch("https://app.example.com/api/v1/acme/files/18", {
  method: "DELETE",
  headers: {
    Authorization: `Bearer ${process.env.VIRESSO_API_KEY}`,
    Accept: "application/json",
  },
});

if (response.status !== 204) {
  throw new Error(`File deletion failed (${response.status}): ${await response.text()}`);
}
// Success has no body. Do not call response.json().
```

An already deleted or unavailable file returns `404`. See [Files API](/docs/api/files#delete-a-file) for permissions, rate limits, and storage behavior.

## Hosted auth tokens

Hosted auth access tokens are only for app user authentication through the hosted auth endpoints. They are not API keys. Use an API key for entry requests, file uploads and deletions, and email API requests. Tokenized file downloads use the returned URL without an API key.

## Related pages

- [API Overview](/docs/api/overview) — Base URL, headers, and error handling.
- [API Authentication](/docs/api/authentication) — API keys and hosted authentication.
- [API Entries](/docs/api/entries) — Entry CRUD reference.
- [Files API](/docs/api/files) — Uploads, deletions, limits, and file responses.
