Shallow Curve

GTM automation without the deep end.

Your agent writes the plays. Shallow Curve runs them on Cloudflare, straight off your PostHog data, with your own API keys. No webhook for every event. No credits to babysit.

Some tools draw a deep line. We drew a shallow curve.

Depth below sanity, first six weeks of GTM automation. Data is anecdotal but heartfelt.

Depth below sanity over the first six weeksThe deep line sinks past sea level to the Marianas Trench through a full Postgres handover, thousands of webhooks, a rebuilt PostHog funnel and running out of credits. The shallow curve dips once, then rises above sea level once PostHog is connected and an agent writes the play.Sea levelAnkle deepOver your headCall the coast guardMarianas TrenchWeek 1Week 2Week 3Week 4Week 5Week 6Pipeline running while you sleep

The deep line

  • Gave the GTM engineer the whole Postgres
  • Webhook number 4,812
  • Rebuilt the PostHog funnel. Again.
  • Out of credits, mid-waterfall

The shallow curve

  • Pointed it at PostHog
  • Your agent wrote the play
  • Pipeline running while you sleep

What changes when you leave the deep end

Getting your data in
The deep end: Give the GTM engineer your entire Postgres. Then fire a webhook every time a user so much as blinks.
Shallow Curve: Connect PostHog, or pick the Postgres tables you are happy to share. New rows start plays on their own.
Paying for lookups
The deep end: Buy credits. Watch credits. Run out of credits halfway through a waterfall at 2am.
Shallow Curve: Bring your own API keys and pay each provider directly. We add no markup and run no meter.
Where it runs
The deep end: Somewhere. Probably fine.
Shallow Curve: Cloudflare Workers, Durable Objects, D1 and R2. A play can sleep for a week and costs nothing while it naps.
The plays you already wrote
The deep end: Stuck where they are.
Shallow Curve: Same authoring API. Change one import, port the SQL, carry on.

A play is one TypeScript file

Your coding agent writes it, you read it, and it runs every time PostHog says someone signed up. Every step is saved as it finishes, so a run that crashes picks up where it stopped instead of paying for the same lookup twice.

These two commands are the entire PostHog integration:

sc sources add posthog --project 12345 --events "user signed up"
sc plays trigger event new-signup \
  --table posthog.events --where "event = 'user signed up'"
new-signup.play.tswritten by your agent, reviewed by you
import { definePlay } from 'shallowcurve';

export default definePlay('new-signup', async (ctx, user) => {
  const site = await ctx.tools.execute({
    id: 'homepage',
    tool: 'contextdev_get_web_scrape_markdown',
    input: { url: `https://${user.domain}` },
  });
  const tier = await ctx.tools.execute({
    id: 'tier',
    tool: 'ai_inference',
    input: { prompt: tierPrompt(site), jsonSchema: TIER },
  });
  await ctx.sleep(2 * HOUR); // a nap, not a server bill
  return { tier: tier.extractedValues.json.get() };
});
Providers wired up
126
Tools your agent can call
1,506
Webhooks you have to write
0

You bring the API keys. We bring the plumbing and never mark it up.

Wade in.

It’s shallow. That’s the whole point.