# Auto-process Supabase uploads with Ittybit

Trigger Ittybit media processing automatically when files are uploaded to Supabase Storage

Upload a file to Supabase Storage and have it automatically processed by Ittybit. A database webhook triggers an Edge Function on insert, that function sends the file to Ittybit, and a webhook callback writes the results back to your Supabase database. Zero manual steps.

## Architecture

1. User uploads to Supabase Storage
2. Database webhook on `storage.objects` insert fires an Edge Function
3. Edge Function POSTs to Ittybit `/tasks` with the file's public URL
4. Ittybit processes the file and sends a webhook on completion
5. A second Edge Function receives the webhook and upserts results into a `media` table

## Create the media table

```sql
create table public.media (
  id uuid primary key default gen_random_uuid(),
  storage_path text not null,
  source_url text not null,
  task_id text,
  status text default 'pending',
  output_url text,
  metadata jsonb,
  created_at timestamptz default now(),
  updated_at timestamptz default now()
);
```

## Edge Function: trigger processing

This function runs when a new row appears in `storage.objects`. It builds the public URL for the uploaded file and creates an Ittybit task.

<CodeGroup labels={["TypeScript", "curl"]}>
```typescript
// supabase/functions/process-upload/index.ts

serve(async (req) => {
const payload = await req.json();
const record = payload.record;

const supabase = createClient(
Deno.env.get("SUPABASE_URL")!,
Deno.env.get("SUPABASE_SERVICE_ROLE_KEY")!,
);

// Build the public URL for the uploaded file
const { data: urlData } = supabase.storage
.from(record.bucket_id)
.getPublicUrl(record.name);

const sourceUrl = urlData.publicUrl;

// Insert a pending row in the media table
const { data: media } = await supabase
.from("media")
.insert({
storage_path: `${record.bucket_id}/${record.name}`,
source_url: sourceUrl,
status: "pending",
})
.select()
.single();

// Create an Ittybit task
const task = await fetch("https://api.ittybit.com/jobs", {
method: "POST",
headers: {
Authorization: `Bearer ${Deno.env.get("ITTYBIT_API_KEY")}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
input: sourceUrl,
kind: "video",
options: {
width: 1280,
format: "mp4",
quality: "high",
},
metadata: {
media_id: media.id,
},
}),
});

const result = await task.json();

// Store the task ID
await supabase
.from("media")
.update({ task_id: result.id, status: "processing" })
.eq("id", media.id);

return new Response(JSON.stringify({ ok: true }), {
headers: { "Content-Type": "application/json" },
});
});

````

```bash
# Test the flow manually by creating a task with a Supabase Storage URL
curl -X POST https://api.ittybit.com/jobs \
  -H "Authorization: Bearer $ITTYBIT_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "input": "https://your-project.supabase.co/storage/v1/object/public/uploads/video.mp4",
    "kind": "video",
    "options": {
      "width": 1280,
      "format": "mp4",
      "quality": "high"
    }
  }'
````

</CodeGroup>

## Wire up the database webhook

In the Supabase Dashboard, go to **Database > Webhooks** and create a new webhook:

- **Table:** `storage.objects`
- **Events:** `INSERT`
- **Type:** Supabase Edge Function
- **Function:** `process-upload`

Every file upload now triggers processing automatically.

## Edge Function: receive Ittybit webhook

This function receives the callback from Ittybit when processing completes and updates your `media` table.

<CodeGroup labels={["TypeScript", "curl"]}>
```typescript
// supabase/functions/ittybit-webhook/index.ts

serve(async (req) => {
const payload = await req.json();

const supabase = createClient(
Deno.env.get("SUPABASE_URL")!,
Deno.env.get("SUPABASE_SERVICE_ROLE_KEY")!,
);

const mediaId = payload.metadata?.media_id;
if (!mediaId) {
return new Response("Missing media_id", { status: 400 });
}

await supabase
.from("media")
.update({
status: payload.status === "completed" ? "completed" : "failed",
output_url: payload.output?.url ?? null,
metadata: payload,
updated_at: new Date().toISOString(),
})
.eq("id", mediaId);

return new Response(JSON.stringify({ ok: true }), {
headers: { "Content-Type": "application/json" },
});
});

````

```bash
# Register your webhook endpoint in the Ittybit dashboard
# or via the API:
curl -X POST https://api.ittybit.com/webhooks \
  -H "Authorization: Bearer $ITTYBIT_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://your-project.supabase.co/functions/v1/ittybit-webhook",
    "events": ["job.succeeded", "job.failed"]
  }'
````

</CodeGroup>

## Deploy the Edge Functions

```bash
supabase functions deploy process-upload
supabase functions deploy ittybit-webhook
```

Set your secrets:

```bash
supabase secrets set ITTYBIT_API_KEY=your_ittybit_api_key
```

`SUPABASE_URL` and `SUPABASE_SERVICE_ROLE_KEY` are available automatically in Edge Functions.

## Test the pipeline

Upload a file to your Supabase Storage bucket and query the media table to watch it progress:

```sql
select id, storage_path, status, output_url, created_at
from media
order by created_at desc
limit 5;
```

The row should move from `pending` to `processing` to `completed`, with `output_url` populated once Ittybit finishes.

## See also

- [Build a user upload pipeline](/guides/build-a-user-upload-pipeline) -- multi-task processing for uploads
- [Process files from S3](/guides/process-files-from-s3) -- use S3-compatible storage as input