> ## Documentation Index
> Fetch the complete documentation index at: https://docs.everabyte.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Laravel and PHP

> Integrate Everabyte storage into a Laravel application: configuration, a small client, streaming uploads, and safe error handling.

The Everabyte API is plain JSON over HTTPS, so Laravel's HTTP client is enough. This guide shows a pattern you can adapt; it is not an official SDK.

## Configuration

Store credentials in `config/services.php` and the environment, never in code.

```php theme={null}
// config/services.php
return [
    'everabyte' => [
        'base_url'   => env('EVERABYTE_BASE_URL', 'https://api.everabyte.io/api'),
        'public_key' => env('EVERABYTE_PUBLIC_KEY'),
        'secret_key' => env('EVERABYTE_SECRET_KEY'),
        'bucket_id'  => env('EVERABYTE_BUCKET_ID'),
        'timeout'    => env('EVERABYTE_TIMEOUT', 600),
    ],
];
```

```dotenv theme={null}
EVERABYTE_BASE_URL=https://api.everabyte.io/api
EVERABYTE_PUBLIC_KEY=pk_your_public_key
EVERABYTE_SECRET_KEY=sk_your_secret_key
EVERABYTE_BUCKET_ID=your-bucket-uuid
```

Never commit `.env`. Use your deployment platform's secret store in production.

## A small client

```php theme={null}
<?php

namespace App\Services;

use Illuminate\Http\Client\PendingRequest;
use Illuminate\Support\Facades\Http;

class EverabyteClient
{
    private function request(): PendingRequest
    {
        return Http::baseUrl(config('services.everabyte.base_url'))
            ->withHeaders([
                'X-Public-Key' => config('services.everabyte.public_key'),
                'X-Secret-Key' => config('services.everabyte.secret_key'),
            ])
            ->acceptJson()
            ->timeout(config('services.everabyte.timeout'));
    }

    public function listFiles(string $bucketId, int $page = 1, int $limit = 20): array
    {
        return $this->request()
            ->get("/files/findAll/{$bucketId}", [
                'page' => $page,
                'limit' => $limit,
            ])
            ->throw()
            ->json('data');
    }

    public function listFolders(string $bucketId, int $page = 1, int $limit = 20): array
    {
        return $this->request()
            ->get("/folders/findAll/{$bucketId}", [
                'page' => $page,
                'limit' => $limit,
            ])
            ->throw()
            ->json('data');
    }
}
```

## Uploading a file

`Http::attach()` streams from disk when you pass a path with a resource, but be explicit about memory.

```php theme={null}
<?php

use Illuminate\Support\Facades\Http;

public function upload(string $path, ?string $folderId = null): string
{
    $bucketId = config('services.everabyte.bucket_id');
    $url = $folderId
        ? "/files/upload/{$bucketId}/{$folderId}"
        : "/files/upload/{$bucketId}";

    $response = Http::baseUrl(config('services.everabyte.base_url'))
        ->withHeaders([
            'X-Public-Key' => config('services.everabyte.public_key'),
            'X-Secret-Key' => config('services.everabyte.secret_key'),
        ])
        ->timeout(600)
        ->attach('file', fopen($path, 'r'), basename($path))
        ->post($url);

    $response->throw();

    return $response->json('data.fileId');
}
```

> [!WARNING]
>
> A multi-file request returns only the first file's result. If you send more than one part, list the folder afterward to discover every `fileId`.

## Downloading a file

Stream to a response instead of buffering the whole file in PHP memory.

```php theme={null}
<?php

use Illuminate\Support\Facades\Http;

public function download(string $fileId)
{
    $response = Http::baseUrl(config('services.everabyte.base_url'))
        ->withHeaders([
            'X-Public-Key' => config('services.everabyte.public_key'),
            'X-Secret-Key' => config('services.everabyte.secret_key'),
        ])
        ->withOptions(['stream' => true])
        ->timeout(600)
        ->get("/files/{$fileId}/stream");

    $response->throw();

    return response()->streamDownload(function () use ($response) {
        $body = $response->toPsrResponse()->getBody();
        while (! $body->eof()) {
            echo $body->read(8192);
        }
    }, 'download.bin');
}
```

Keep the filename you present under your own control. Do not pass a user-supplied filename straight into a `Content-Disposition` header without sanitizing it.

## Error handling

Laravel's `throw()` raises `RequestException` for 4xx/5xx. Decode the envelope yourself so you can branch on `code`.

```php theme={null}
<?php

use Illuminate\Http\Client\RequestException;

try {
    $client->listFiles(config('services.everabyte.bucket_id'));
} catch (RequestException $exception) {
    $response = $exception->response;
    $body = $response?->json() ?? [];

    $code = $body['code'] ?? null;
    $status = $body['status'] ?? $body['statusCode'] ?? $response?->status();

    report(new \RuntimeException("Everabyte {$status} {$code}"));

    if (in_array($status, [429], true) || $status >= 500) {
        // Retry with backoff via a queued job.
    }
}
```

Rules that matter:

| Status | Action |
| - | - |
| `429` | Retry with exponential backoff and jitter |
| `5xx` | Retry, bounded |
| `401` | Do not retry; check key state |
| `402` | Do not retry; billing or capacity |
| `403` | Do not retry; scope, surface, or bucket restriction |
| `404` | Do not retry; check IDs |

## Queues and timeouts

Large uploads do not belong in a synchronous web request.

* Push uploads to a queued job with a generous timeout.
* Set `retry_after` above your job timeout so jobs are not duplicated.
* Make the job idempotent by recording the resulting `fileId`.
* Do not retry a failed upload blindly; check whether the object already exists.

```php theme={null}
// config/queue.php (example for the redis connection)
'redis' => [
    'driver' => 'redis',
    'connection' => 'default',
    'queue' => 'default',
    'retry_after' => 900,
    'block_for' => null,
],
```

## Security checklist

* [ ] Credentials only from environment or a secret manager.
* [ ] Key scoped to the operations you actually use.
* [ ] Key restricted to the buckets you actually use.
* [ ] Uploaded files treated as untrusted content.
* [ ] File type and size validated before upload.
* [ ] Filenames sanitized for display and headers.
* [ ] No secrets in logs, including exception traces.
* [ ] Separate keys per environment.

## Related

> **[Upload files](https://docs.everabyte.com/storage/upload-files.md)**
>
> Upload semantics and limits.

> **[Download files](https://docs.everabyte.com/storage/download-files.md)**
>
> Streaming and ranges.

> **[Authentication](https://docs.everabyte.com/authentication/overview.md)**
>
> Key model and scopes.

> **[Error codes](https://docs.everabyte.com/errors/error-codes.md)**
>
> Stable codes.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.