echo "SUPABASE_URL: $SUPABASE_URL" echo "SUPABASE_PROJECT_REF: $SUPABASE_PROJECT_REF" echo "SUPABASE_ANON_KEY: $SUPABASE_ANON_KEY" echo "SUPABASE_SERVICE_ROLE_KEY: $SUPABASE_SERVICE_ROLE_KEY" echo "SUPABASE_DB_URL: $SUPABASE_DB_URL" echo "SUPABASE_DB_PASSWORD: $SUPABASE_DB_PASSWORD"
SUPABASE_URL: https://<ref>.supabase.co SUPABASE_PROJECT_REF: <ref> SUPABASE_ANON_KEY: <redacted:SUPABASE_ANON_KEY> SUPABASE_SERVICE_ROLE_KEY: <redacted:SUPABASE_SERVICE_ROLE_KEY> SUPABASE_DB_URL: <redacted:SUPABASE_DB_URL> SUPABASE_DB_PASSWORD: <redacted:SUPABASE_DB_PASSWORD>
# Check the signups table structure psql "$SUPABASE_DB_URL" -c "\d public.signups"
Table "public.signups"
Column | Type | Collation | Nullable | Default
------------+--------------------------+-----------+----------+----------------------------------
id | bigint | | not null | generated by default as identity
email | text | | not null |
plan | text | | not null | 'free'::text
created_at | timestamp with time zone | | not null | now()
Indexes:
"signups_pkey" PRIMARY KEY, btree (id)
# Check if pg_net extension exists psql "$SUPABASE_DB_URL" -c "SELECT * FROM pg_extension WHERE extname = 'pg_net';" # Check if webhook_receipts table exists psql "$SUPABASE_DB_URL" -c "\d public.webhook_receipts" 2>&1
oid | extname | extowner | extnamespace | extrelocatable | extversion | extconfig | extcondition
-----+---------+----------+--------------+----------------+------------+-----------+--------------
(0 rows)
Table "public.webhook_receipts"
Column | Type | Collation | Nullable | Default
-------------+--------------------------+-----------+----------+----------------------------------
id | bigint | | not null | generated by default as identity
received_at | timestamp with time zone | | not null | now()
payload | jsonb | | not null |
Indexes:
"webhook_receipts_pkey" PRIMARY KEY, btree (id)
# Enable pg_net extension psql "$SUPABASE_DB_URL" -c "CREATE EXTENSION IF NOT EXISTS pg_net SCHEMA extensions;"
CREATE EXTENSION
# Verify pg_net is installed psql "$SUPABASE_DB_URL" -c "SELECT net.available();"
ERROR: function net.available() does not exist
LINE 1: SELECT net.available();
^
HINT: No function matches the given name and argument types. You might need to add explicit type casts.
# Check what functions are available in pg_net psql "$SUPABASE_DB_URL" -c "SELECT routine_name FROM information_schema.routines WHERE routine_schema = 'net';"
routine_name ------------------------------- check_worker_is_up _await_response _urlencode_string _encode_url_with_params_array worker_restart wait_until_running wake http_get http_post http_delete _http_collect_response http_collect_response (12 rows)
# Check pg_net worker status
psql "$SUPABASE_DB_URL" -c "SELECT net.check_worker_is_up();"
# Check http_post function signature
psql "$SUPABASE_DB_URL" -c "SELECT pg_get_function_identity_arguments('net.http_post'::regproc);" check_worker_is_up
--------------------
(1 row)
pg_get_function_identity_arguments
---------------------------------------------------------------------------------
url text, body jsonb, params jsonb, headers jsonb, timeout_milliseconds integer
(1 row)
psql "$SUPABASE_DB_URL" <<'SQL'
-- Create the trigger function that fires on INSERT into signups
CREATE OR REPLACE FUNCTION public.send_signups_webhook()
RETURNS trigger
LANGUAGE plpgsql
SECURITY DEFINER
AS $$
DECLARE
_payload jsonb;
_url text;
_headers jsonb;
BEGIN
-- Build the payload with email and plan
_payload := jsonb_build_object(
'email', NEW.email,
'plan', NEW.plan
);
_url := current_setting('app.settings.supabase_url') || '/functions/v1/hook-recorder';
_headers := jsonb_build_object(
'Content-Type', 'application/json',
'Authorization', current_setting('app.settings.service_role_key')
);
-- Fire async HTTP POST via pg_net
PERFORM net.http_post(
url := _url,
body := _payload,
params := NULL::jsonb,
headers := _headers,
timeout_milliseconds := 5000
);
RETURN NEW;
END;
$$;
-- Create the trigger on the signups table
CREATE TRIGGER trg_send_signups_webhook
AFTER INSERT ON public.signups
FOR EACH ROW
EXECUTE FUNCTION public.send_signups_webhook();
SQLCREATE FUNCTION CREATE TRIGGER
# Set the custom GUC settings that the trigger function reads psql "$SUPABASE_DB_URL" -c " ALTER DATABASE postgres SET app.settings.supabase_url = '$SUPABASE_URL'; ALTER DATABASE postgres SET app.settings.service_role_key = '$SUPABASE_SERVICE_ROLE_KEY'; "
ERROR: permission denied to set parameter "app.settings.supabase_url"
# Try connecting directly as postgres user (not through pooler)
DB_URL_DIRECT="postgresql://postgres:<redacted>@db.${SUPABASE_PROJECT_REF}.supabase.co:5432/postgres"
psql "$DB_URL_DIRECT" -c "
ALTER DATABASE postgres SET app.settings.supabase_url = '$SUPABASE_URL';
ALTER DATABASE postgres SET app.settings.service_role_key = '$SUPABASE_SERVICE_ROLE_KEY';
"psql: error: connection to server at "db.<ref>.supabase.co" (2600:1f18:5b28:201:95fb:32bf:d264:4d3e), port 5432 failed: Network is unreachable Is the server running on that host and accepting TCP/IP connections?
# Simpler approach: hardcode the values directly in the function
psql "$SUPABASE_DB_URL" <<SQL
CREATE OR REPLACE FUNCTION public.send_signups_webhook()
RETURNS trigger
LANGUAGE plpgsql
SECURITY DEFINER
AS \\\$\\\$
DECLARE
_payload jsonb;
_url text;
_headers jsonb;
BEGIN
_payload := jsonb_build_object(
'email', NEW.email,
'plan', NEW.plan
);
_url := '${SUPABASE_URL}/functions/v1/hook-recorder';
_headers := jsonb_build_object(
'Content-Type', 'application/json',
'Authorization', '${SUPABASE_SERVICE_ROLE_KEY}'
);
PERFORM net.http_post(
url := _url,
body := _payload,
params := NULL::jsonb,
headers := _headers,
timeout_milliseconds := 5000
);
RETURN NEW;
END;
\\\$\\\$;
SQLCOMMIT
invalid command \$
ERROR: syntax error at or near "DECLARE"
LINE 6: DECLARE
^
ERROR: syntax error at or near "_url"
LINE 1: _url text;
^
ERROR: syntax error at or near "_headers"
LINE 1: _headers jsonb;
^
ERROR: syntax error at or near "_payload"
LINE 2: _payload := jsonb_build_object(
^
ERROR: syntax error at or near "_url"
LINE 1: _url := 'https://<ref>.supabase.co/functions/...
^
ERROR: syntax error at or near "_headers"
LINE 1: _headers := jsonb_build_object(
^
ERROR: syntax error at or near "PERFORM"
LINE 1: PERFORM net.http_post(
^
ERROR: syntax error at or near "RETURN"
LINE 1: RETURN NEW;
^
WARNING: there is no transaction in progress
invalid command \$
cat > /tmp/webhook_func.sql << 'EOSQL'
CREATE OR REPLACE FUNCTION public.send_signups_webhook()
RETURNS trigger
LANGUAGE plpgsql
SECURITY DEFINER
AS $$
DECLARE
_payload jsonb;
_url text;
_headers jsonb;
BEGIN
_payload := jsonb_build_object(
'email', NEW.email,
'plan', NEW.plan
);
_url := '${SUPABASE_URL}/functions/v1/hook-recorder';
_headers := jsonb_build_object(
'Content-Type', 'application/json',
'Authorization', '${SUPABASE_SERVICE_ROLE_KEY}'
);
PERFORM net.http_post(
url := _url,
body := _payload,
params := NULL::jsonb,
headers := _headers,
timeout_milliseconds := 5000
);
RETURN NEW;
END;
$$;
EOSQL
# Now substitute the environment variables
envsubst < /tmp/webhook_func.sql > /tmp/webhook_func_resolved.sql
cat /tmp/webhook_func_resolved.sqlbash: line 38: envsubst: command not found
cat > /tmp/webhook_func.sql << 'EOSQL'
CREATE OR REPLACE FUNCTION public.send_signups_webhook()
RETURNS trigger
LANGUAGE plpgsql
SECURITY DEFINER
AS $$
DECLARE
_payload jsonb;
_url text;
_headers jsonb;
BEGIN
_payload := jsonb_build_object(
'email', NEW.email,
'plan', NEW.plan
);
_url := '__SUPABASE_URL__/functions/v1/hook-recorder';
_headers := jsonb_build_object(
'Content-Type', 'application/json',
'Authorization', '__SERVICE_ROLE_KEY__'
);
PERFORM net.http_post(
url := _url,
body := _payload,
params := NULL::jsonb,
headers := _headers,
timeout_milliseconds := 5000
);
RETURN NEW;
END;
$$;
EOSQL
# Substitute placeholders with actual values using sed
sed -i "s|__SUPABASE_URL__|${SUPABASE_URL}|g" /tmp/webhook_func.sql
sed -i "s|__SERVICE_ROLE_KEY__|${SUPABASE_SERVICE_ROLE_KEY}|g" /tmp/webhook_func.sql
cat /tmp/webhook_func.sqlCREATE OR REPLACE FUNCTION public.send_signups_webhook()
RETURNS trigger
LANGUAGE plpgsql
SECURITY DEFINER
AS $$
DECLARE
_payload jsonb;
_url text;
_headers jsonb;
BEGIN
_payload := jsonb_build_object(
'email', NEW.email,
'plan', NEW.plan
);
_url := 'https://<ref>.supabase.co/functions/v1/hook-recorder';
_headers := jsonb_build_object(
'Content-Type', 'application/json',
'Authorization', '<redacted:SUPABASE_SERVICE_ROLE_KEY>'
);
PERFORM net.http_post(
url := _url,
body := _payload,
params := NULL::jsonb,
headers := _headers,
timeout_milliseconds := 5000
);
RETURN NEW;
END;
$$;
psql "$SUPABASE_DB_URL" -f /tmp/webhook_func.sql
CREATE FUNCTION
# Verify the trigger exists psql "$SUPABASE_DB_URL" -c "SELECT tgname, tgenabled FROM pg_trigger WHERE tgrelid = 'public.signups'::regclass AND NOT tgisinternal;" # Verify the function source psql "$SUPABASE_DB_URL" -c "\df+ public.send_signups_webhook"
tgname | tgenabled
--------------------------+-----------
trg_send_signups_webhook | O
(1 row)
List of functions
Schema | Name | Result data type | Argument data types | Type | Volatility | Parallel | Owner | Security | Leakproof? | Access privileges | Language | Internal name | Description
--------+----------------------+------------------+---------------------+------+------------+----------+----------+----------+------------+--------------------------+----------+---------------+-------------
public | send_signups_webhook | trigger | | func | volatile | unsafe | postgres | definer | no | =X/postgres +| plpgsql | |
| | | | | | | | | | postgres=X/postgres +| | |
| | | | | | | | | | anon=X/postgres +| | |
| | | | | | | | | | authenticated=X/postgres+| | |
| | | | | | | | | | service_role=X/postgres | | |
(1 row)
# Check current state of both tables psql "$SUPABASE_DB_URL" -c "SELECT count(*) AS signups_count FROM public.signups;" psql "$SUPABASE_DB_URL" -c "SELECT count(*) AS receipts_count FROM public.webhook_receipts;"
signups_count
---------------
0
(1 row)
receipts_count
----------------
0
(1 row)
# Perform the test INSERT
psql "$SUPABASE_DB_URL" -c "INSERT INTO public.signups (email, plan) VALUES ('test-user@example.com', 'pro');"INSERT 0 1
# Wait a few seconds for async delivery, then check sleep 5 psql "$SUPABASE_DB_URL" -c "SELECT * FROM public.webhook_receipts ORDER BY id;"
id | received_at | payload ----+-------------+--------- (0 rows)
# Check pg_net request status psql "$SUPABASE_DB_URL" -c "SELECT * FROM net._http_response;" 2>&1 psql "$SUPABASE_DB_URL" -c "SELECT * FROM net.http_request_queue;" 2>&1
id | status_code | content_type | headers | content | timed_out | error_msg | created
----+-------------+------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+--------------------------------------------------------------------------------------------+-----------+-----------+-------------------------------
1 | 401 | application/json | {"date": "Sun, 26 Jul 2026 03:48:01 GMT", "vary": "Accept-Encoding", "cf-ray": "a2106d247efad91c-IAD", "server": "cloudflare", "alt-svc": "h3=\":443\"; ma=86400", "set-cookie": "__cf_bm=BQEJkbR_gNMIv3hqB7aXkDZDN7kW7npdarI2w.IbYAc-1785037681.3604321-1.0.1.1-QCAffcSq47KJC1nZ55z6ck5KFyDCMVmdkTqbTVZRQ22se0EUBM9i0jVEZGgqc5gjtWGznkZ4KI62JB_exJOSHd5mdbnz__i9VU7HK1EaaJ5HXLTIymW7vgm6PpYSWVCZ; HttpOnly; SameSite=None; Secure; Path=/; Domain=supabase.co; Expires=Sun, 26 Jul 2026 04:18:01 GMT", "x-served-by": "supabase-edge-runtime", "content-type": "application/json", "sb-error-code": "UNAUTHORIZED_INVALID_JWT_FORMAT", "sb-request-id": "019f9c89-72d3-7f99-9bf6-71c7c9881813", "sb-project-ref": "<ref>", "cf-cache-status": "DYNAMIC", "x-sb-edge-region": "us-east-1", "sb-gateway-version": "1", "strict-transport-security": "max-age=31536000; includeSubDomains; preload", "access-control-allow-origin": "*", "access-control-allow-headers": "authorization, x-client-info, apikey"} | {"code":"UNAUTHORIZED_INVALID_JWT_FORMAT","message":"Auth header is not 'Bearer {token}'"} | f | | 2026-07-26 03:48:01.351067+00
(1 row)
id | method | url | headers | body | timeout_milliseconds
----+--------+-----+---------+------+----------------------
(0 rows)
# Fix the function to use Bearer prefix
cat > /tmp/webhook_func.sql << 'EOSQL'
CREATE OR REPLACE FUNCTION public.send_signups_webhook()
RETURNS trigger
LANGUAGE plpgsql
SECURITY DEFINER
AS $$
DECLARE
_payload jsonb;
_url text;
_headers jsonb;
BEGIN
_payload := jsonb_build_object(
'email', NEW.email,
'plan', NEW.plan
);
_url := '__SUPABASE_URL__/functions/v1/hook-recorder';
_headers := jsonb_build_object(
'Content-Type', 'application/json',
'Authorization', 'Bearer __SERVICE_ROLE_KEY__'
);
PERFORM net.http_post(
url := _url,
body := _payload,
params := NULL::jsonb,
headers := _headers,
timeout_milliseconds := 5000
);
RETURN NEW;
END;
$$;
EOSQL
sed -i "s|__SUPABASE_URL__|${SUPABASE_URL}|g" /tmp/webhook_func.sql
sed -i "s|__SERVICE_ROLE_KEY__|${SUPABASE_SERVICE_ROLE_KEY}|g" /tmp/webhook_func.sql
psql "$SUPABASE_DB_URL" -f /tmp/webhook_func.sqlCREATE FUNCTION
# Do a second test insert
psql "$SUPABASE_DB_URL" -c "INSERT INTO public.signups (email, plan) VALUES ('webhook-test-2@demo.com', 'enterprise');"
# Wait for async delivery
sleep 5
# Check webhook_receipts
psql "$SUPABASE_DB_URL" -c "SELECT * FROM public.webhook_receipts ORDER BY id;"INSERT 0 1
id | received_at | payload
----+-------------------------------+------------------------------------------------------------
2 | 2026-07-26 03:48:20.434681+00 | {"plan": "enterprise", "email": "webhook-test-2@demo.com"}
(1 row)
4 earlier narration turn(s) are in the raw transcript.
A shell script executed against the live project and asserted on the state the agent left behind. No model judged another model.
Raw transcript · Task definition and verifier · Every attempt