Todos os SDKs da AWS funcionam. Basta informar o **endpoint** (informado na ativação) e usar `forcePathStyle` (ou equivalente). Credenciais em variáveis de ambiente, nunca no código.

```
export S3_ENDPOINT=https://ENDPOINT-DA-API
export AWS_ACCESS_KEY_ID=SUA_ACCESS_KEY
export AWS_SECRET_ACCESS_KEY=SUA_SECRET_KEY
export AWS_REGION=us-east-1
```

## Node.js (`@aws-sdk/client-s3`)

```js
import { S3Client, PutObjectCommand, GetObjectCommand } from "@aws-sdk/client-s3";
import { getSignedUrl } from "@aws-sdk/s3-request-presigner";
import fs from "node:fs";

const s3 = new S3Client({
  endpoint: process.env.S3_ENDPOINT,
  region: process.env.AWS_REGION,
  forcePathStyle: true,
});

// upload
await s3.send(new PutObjectCommand({ Bucket: "midia-site", Key: "img/foto.jpg", Body: fs.createReadStream("foto.jpg"), ContentType: "image/jpeg" }));

// link temporário (15 min)
const url = await getSignedUrl(s3, new GetObjectCommand({ Bucket: "backups-vps", Key: "relatorio.pdf" }), { expiresIn: 900 });
```

## Python (`boto3`)

```python
import boto3, os

s3 = boto3.client("s3", endpoint_url=os.environ["S3_ENDPOINT"])

s3.upload_file("foto.jpg", "midia-site", "img/foto.jpg", ExtraArgs={"ContentType": "image/jpeg"})
s3.download_file("backups-vps", "relatorio.pdf", "relatorio.pdf")
url = s3.generate_presigned_url("get_object", Params={"Bucket": "backups-vps", "Key": "relatorio.pdf"}, ExpiresIn=900)
```

## PHP (`aws/aws-sdk-php`)

```php
$s3 = new Aws\S3\S3Client([
  'version' => 'latest', 'region' => getenv('AWS_REGION'),
  'endpoint' => getenv('S3_ENDPOINT'), 'use_path_style_endpoint' => true,
]);
$s3->putObject(['Bucket' => 'midia-site', 'Key' => 'img/foto.jpg', 'SourceFile' => 'foto.jpg']);
$cmd = $s3->getCommand('GetObject', ['Bucket' => 'backups-vps', 'Key' => 'relatorio.pdf']);
$url = (string) $s3->createPresignedRequest($cmd, '+15 minutes')->getUri();
```

**Laravel:** no `config/filesystems.php`, disco `s3` com `'endpoint' => env('S3_ENDPOINT')` e `'use_path_style_endpoint' => true`; depois `Storage::disk('s3')->put(...)`.

**WordPress:** plugins de "offload media" (ex.: WP Offload Media, Media Cloud) aceitam provedor "S3 compatível" com endpoint personalizado.

## Erros comuns

- `SignatureDoesNotMatch`: região diferente entre cliente e servidor, ou relógio fora de hora.
- `PermanentRedirect` / tentativa de acessar `bucket.amazonaws.com`: faltou o endpoint ou o path-style.
- Upload grande falhando: use o upload multipart do SDK (`Upload` no Node, `upload_file` já faz isso no Python).
