Milestone 1: Add Numbers Zapier app, /v1/add, developer setup guide

packages/verae-activate is pushable with zapier-platform 19.1.0: local
Add Numbers (number1+number2=sum) needs no hosted API. zapier-platform
validate is clean (0 warnings). zappier POST /v1/add is a free metered
endpoint for the optional hosted path.

Tests: verae-activate 7/7, zappier 176/176, verae-zapier 5/5, middleware
gate 1 11/11. Morning steps: docs/04-activate/SETUP-ZAPIER-DEVELOPER.md

Learned: perform runs on Zapier cloud so arithmetic needs no public URL;
CLI and zapier-platform-core majors must match; do not mix zapier-sdk.
This commit is contained in:
George Lambert 2026-09-09 02:41:34 -04:00
parent b4150c8250
commit b814501441
18 changed files with 1452 additions and 1 deletions

View file

@ -58,6 +58,27 @@ paths:
responses:
'200':
description: OK
/v1/add:
post:
operationId: add
summary: Add two numbers (activate-now sample)
requestBody:
required: true
content:
application/json:
schema:
type: object
required: [number1, number2]
properties:
number1:
type: number
description: First addend
number2:
type: number
description: Second addend
responses:
'200':
description: OK
/v1/usage:
get:
operationId: usage

View file

@ -157,6 +157,16 @@ export function buildApp(deps: AppDeps = {}): {
res.json({ output: text.toUpperCase(), quote: res.locals.quote });
});
app.post('/v1/add', meter('add', usage, pricing), (req, res) => {
const number1 = Number(req.body?.number1);
const number2 = Number(req.body?.number2);
if (!Number.isFinite(number1) || !Number.isFinite(number2)) {
res.status(400).json({ error: 'number1 and number2 must be finite numbers' });
return;
}
res.json({ number1, number2, sum: number1 + number2, quote: res.locals.quote });
});
app.post('/v1/storage', parseMetadata, meter('storage', usage, pricing), (req, res) => {
const metadata = res.locals.parsedMetadata as Record<string, unknown>;
const files = (req.files as Express.Multer.File[]) ?? [];

View file

@ -63,6 +63,7 @@ export const DEFAULT_RATE_CARD: RateCard = {
status: { kind: 'free' },
'storage-list': { kind: 'free' },
transform: { kind: 'fixed', fixedCents: 4 },
add: { kind: 'free' },
storage: { kind: 'variable', baseCents: 10, perKbCents: 1, perMbCents: 50 },
},
};

View file

@ -18,6 +18,25 @@ describe('Zappier API', () => {
expect(res.body.quote.totalCents).toBe(0);
});
it('POST /v1/add returns the sum and is free', async () => {
const { app } = buildApp();
const res = await request(app)
.post('/v1/add')
.set('x-api-key', KEY)
.send({ number1: 2, number2: 3 });
expect(res.status).toBe(200);
expect(res.body.sum).toBe(5);
expect(res.body.number1).toBe(2);
expect(res.body.number2).toBe(3);
expect(res.body.quote.totalCents).toBe(0);
});
it('POST /v1/add rejects missing fields', async () => {
const { app } = buildApp();
const res = await request(app).post('/v1/add').set('x-api-key', KEY).send({ number1: 1 });
expect(res.status).toBe(400);
});
it('POST /v1/transform uppercases text at the multiplied fixed price', async () => {
const { app } = buildApp();
const res = await request(app)