Repository navigation
feat(api): strict api key validation with issue paths - #227
Merged
Merged
Conversation
A 400 from the form, branding and destinations routes now carries
`issues: [{ path, message, code }]` with every zod issue, `path` being the
dot path inside the request body (`config.steps.2.question`). `message`
stays the first issue's, so a client that reads only `message` sees no
change. One shared `parse` replaces the three copies that kept only the
first issue.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Josue Hernandez <josue@daptatech.com>
…/forms A config copied from another form carries that form's `formGuid` and `formSignature`, and keeping them aims the new form at the original's mirror form in the portal. Creating now strips them for every caller, with the same helper duplicate uses, now exported from @quill/db. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Josue Hernandez <josue@daptatech.com>
With `via: 'api_key'` only, a form config is refused with UNSUPPORTED_FIELD and the exact path when it carries a field the editor has no control for (the spec's research list), a step without a question is refused with EMPTY_QUESTION, and a PUT config without `steps` answers 400 STEPS_REQUIRED before anything is written. The webhook fields are also refused on PUT /v1/forms/:id/destinations. The issues join the schema's in one 400. The value the editor itself writes passes (the derived flowGroup, "Continue" on a message, reveal.enabled and scheduler.prefill true, the brand kit's own client logos): every builder step carries those, and so does the normalized config this API answers with, so refusing them would refuse a form read and sent straight back. A key's config is saved as `normalizeConfig` returns it, and the response carries it. A session saves exactly as before, legacy fields included, with an explicit regression test. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Josue Hernandez <josue@daptatech.com>
Changing a step's type in the builder keeps what createEmptyStep seeded (buttonText "Continue", reveal.enabled true, scheduler.prefill true) and only resets flowGroup. A builder form with a retyped step was refused with UNSUPPORTED_FIELD when an API key sent it straight back. Those seeded values now pass on any step type; flowGroup stays tied to the step's type. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Josue Hernandez <josue@daptatech.com>
PUT /v1/forms/:id drops destinations from the draft, so refusing webhook signatureHeader/timeoutMs there refused every edit of a form whose live webhook already carried one, and removing it from the body never helped. The webhook rule now runs where destinations are written: POST /v1/forms and PUT /v1/forms/:id/destinations. The brand kit client logos comparison used JSON.stringify, so the same logos with their keys in another order were refused. It now uses isDeepStrictEqual. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Josue Hernandez <josue@daptatech.com>
This was referenced Oct 13, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
API v1 validation errors now list every issue with its dot path, and writes made with an API key are held to what the form editor can show. A form created or edited by API stays fully editable in the builder.
Changes
issues[]with dot paths (for exampleconfig.steps.2.question) on admin CRUD, branding and destinations.buttonText,reveal.enabled,scheduler.prefill) are accepted on any step type, matching the builder's type change.STEPS_REQUIREDwrites nothing; the normalized config is returned.formGuidandformSignatureare stripped on POST for every caller.isDeepStrictEqual), not key order.How it was tested
pnpm typecheckgreen (Node 22).pnpm testgreen: api 768, web 1248, db 353, engine 436, destinations 125, types 59, shared 83, notifications 57.pnpm changeset statusOK.Review notes
Fixed by the independent review:
signatureHeader/timeoutMsrefused on PUT forms/:id even though destinations are not saved there (7e2c000).Known edges, unchanged by design: stored logos refused once the kit changes; blank-title message/scheduler steps refused (
EMPTY_QUESTION); paths carry theconfig.prefix.🤖 Generated with Claude Code