Skip to content

fix(sendgrid): fix active field coercion, add pagination, tighten output typing#5368

Merged
waleedlatif1 merged 10 commits into
stagingfrom
worktree-sendgrid-validate
Jul 2, 2026
Merged

fix(sendgrid): fix active field coercion, add pagination, tighten output typing#5368
waleedlatif1 merged 10 commits into
stagingfrom
worktree-sendgrid-validate

Conversation

@waleedlatif1

Copy link
Copy Markdown
Collaborator

Summary

  • Fixed the active field on Create Template Version being sent to SendGrid as the string "true"/"false" instead of the required int 0/1 — this would fail on every default (non-advanced-mode) run
  • Added missing authMode: ApiKey on the SendGrid block
  • Added pageToken/nextPageToken pagination support to List Templates and List All Lists (SendGrid's page_token cursor, parsed from _metadata.next)
  • Fixed nullable output fields to consistently use ?? null / ?? [] with optional: true across get_contact, search_contacts, remove_contacts_from_list, create_template_version, add_contact, send_mail
  • Removed a dead data.templates fallback in list_templates (the API only ever returns result)
  • Removed unused UpdateContactParams/UpdateListParams/UpdateTemplateParams dead types; made CreateTemplateParams.generation optional to match actual tool behavior

Validated the full 16-tool integration against SendGrid's v3 API docs (Mail Send, Contacts, Lists, Transactional Templates/Versions) — all endpoints, params, and response shapes are aligned with current live API behavior. No fields were renamed or removed.

Type of Change

  • Bug fix
  • Improvement

Testing

Tested manually (tsc + biome clean on all touched files)

Checklist

  • Code follows project style guidelines
  • Self-reviewed my changes
  • Tests added/updated and passing
  • No new warnings introduced
  • I confirm that I have read and agree to the terms outlined in the Contributor License Agreement (CLA)

@vercel

vercel Bot commented Jul 2, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

1 Skipped Deployment
Project Deployment Actions Updated (UTC)
docs Skipped Skipped Jul 2, 2026 6:02pm

Request Review

@cursor

cursor Bot commented Jul 2, 2026

Copy link
Copy Markdown

PR Summary

Low Risk
Scoped to SendGrid tools and block config with API-alignment fixes; no auth, billing, or broad executor changes beyond a one-route validation baseline bump.

Overview
Fixes Create Template Version so the active flag is sent as SendGrid’s required 0/1 integers via shared toActiveFlag (block UI strings like "true"/"false" no longer break default runs). The SendGrid block now declares authMode: ApiKey, wires page tokens for List All Lists and List Templates, and exposes nextPageToken for follow-up pages.

Tool-layer updates add page_token query support and parse cursors from _metadata.next, always send page_size on list templates, drop the unused data.templates fallback, and map add-contact custom fields into custom_fields instead of safeAssign. Nullable API fields are normalized (?? null / ?? []) with optional: true on tool outputs; shared types gain pagination params and lose unused update-param types. The API route validation baseline ticks 883 → 884 routes.

Reviewed by Cursor Bugbot for commit 4d3663e. Configure here.

Comment thread apps/sim/blocks/blocks/sendgrid.ts Outdated
Comment thread apps/sim/blocks/blocks/sendgrid.ts Outdated
@greptile-apps

greptile-apps Bot commented Jul 2, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR fixes a collection of real bugs in the SendGrid integration and adds cursor-based pagination to two list operations. All changes are tightly scoped to the SendGrid tool layer and its block definition.

  • active coercion fix: toActiveFlag() now converts any boolean/string/number representation of the flag to the 0/1 integer SendGrid requires, fixing the primary bug that caused every default-mode create_template_version call to fail. The helper is shared between the tool body and the block's pre-transform so both layers stay in sync.
  • Pagination: list_templates and list_all_lists now accept a pageToken parameter and return nextPageToken parsed from the cursor URL in _metadata.next, with a safe try/catch around the URL construction.
  • Nullable output cleanup: jobId, messageId, htmlContent, plainContent, updatedAt, customFields, and contactCount are now consistently guarded with ?? null / ?? [] and marked optional: true, eliminating silent undefined output fields across six tools.

Confidence Score: 5/5

Safe to merge — the changes fix confirmed bugs against the live SendGrid v3 API with no functional regressions introduced.

Every changed code path corrects a documented mismatch with the SendGrid API (integer active flag, custom_fields nesting, nullable outputs). The toActiveFlag helper is straightforward and exhaustively covers all source types. Pagination parsing is guarded with try/catch. No new external surface area is introduced beyond the two pagination query parameters.

No files require special attention — all changes are self-contained within the SendGrid tool and block files.

Important Files Changed

Filename Overview
apps/sim/tools/sendgrid/create_template_version.ts Introduces toActiveFlag helper coercing boolean/string/number active to 0
apps/sim/blocks/blocks/sendgrid.ts Adds authMode: ApiKey, wires active through toActiveFlag in the block transform, and exposes listPageToken/templatePageToken pagination fields.
apps/sim/tools/sendgrid/list_templates.ts Adds pageToken param and nextPageToken output; correctly parses cursor from _metadata.next URL inside a try/catch. Removes dead data.templates fallback.
apps/sim/tools/sendgrid/list_all_lists.ts Adds pageToken param and nextPageToken output with safe URL parsing.
apps/sim/tools/sendgrid/add_contact.ts Replaces safeAssign(contact, customFields) with contact.custom_fields = customFields, matching the SendGrid API spec.
apps/sim/tools/sendgrid/types.ts Removes three dead interfaces; tightens SendGridTemplateVersionRequest.active to number only; adds nextPageToken to ListsResult and TemplatesResult.
apps/sim/tools/sendgrid/get_contact.ts Adds ?? [] / ?? null fallbacks for list_ids and custom_fields and marks those outputs optional: true.
apps/sim/tools/sendgrid/search_contacts.ts Marks contactCount nullable with ?? null fallback and optional: true in outputs.
apps/sim/tools/sendgrid/remove_contacts_from_list.ts Marks jobId nullable and optional: true in outputs to match async API behavior.
apps/sim/tools/sendgrid/send_mail.ts Marks messageId output optional: true; no functional change.
scripts/check-api-validation-contracts.ts Bumps route baseline from 883 to 884 to account for the new API route introduced by this change.

Sequence Diagram

%%{init: {'theme': 'neutral'}}%%
sequenceDiagram
    participant Block as SendGrid Block
    participant Tool as Tool (create_template_version)
    participant SG as SendGrid v3 API

    Note over Block: active = 'true' (string from dropdown)
    Block->>Block: toActiveFlag(active) → 1
    Block->>Tool: "params { active: 1 }"
    Tool->>Tool: toActiveFlag(params.active) → 1
    Tool->>SG: "POST /v3/templates/{id}/versions { active: 1 }"
    SG-->>Tool: "201 { id, active: 1, ... }"
    Tool-->>Block: "{ active: true, htmlContent: null, ... }"

    Note over Block,SG: Pagination flow
    Block->>Tool: "list_templates { pageSize: 20 }"
    Tool->>SG: "GET /v3/templates?page_size=20"
    SG-->>Tool: "{ result: [...], _metadata: { next: '...?page_token=abc' } }"
    Tool-->>Block: "{ templates: [...], nextPageToken: 'abc' }"
    Block->>Tool: "list_templates { pageToken: 'abc', pageSize: 20 }"
    Tool->>SG: "GET /v3/templates?page_size=20&page_token=abc"
    SG-->>Tool: "{ result: [...], _metadata: {} }"
    Tool-->>Block: "{ templates: [...], nextPageToken: null }"
Loading
%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%%
sequenceDiagram
    participant Block as SendGrid Block
    participant Tool as Tool (create_template_version)
    participant SG as SendGrid v3 API

    Note over Block: active = 'true' (string from dropdown)
    Block->>Block: toActiveFlag(active) → 1
    Block->>Tool: "params { active: 1 }"
    Tool->>Tool: toActiveFlag(params.active) → 1
    Tool->>SG: "POST /v3/templates/{id}/versions { active: 1 }"
    SG-->>Tool: "201 { id, active: 1, ... }"
    Tool-->>Block: "{ active: true, htmlContent: null, ... }"

    Note over Block,SG: Pagination flow
    Block->>Tool: "list_templates { pageSize: 20 }"
    Tool->>SG: "GET /v3/templates?page_size=20"
    SG-->>Tool: "{ result: [...], _metadata: { next: '...?page_token=abc' } }"
    Tool-->>Block: "{ templates: [...], nextPageToken: 'abc' }"
    Block->>Tool: "list_templates { pageToken: 'abc', pageSize: 20 }"
    Tool->>SG: "GET /v3/templates?page_size=20&page_token=abc"
    SG-->>Tool: "{ result: [...], _metadata: {} }"
    Tool-->>Block: "{ templates: [...], nextPageToken: null }"
Loading

Reviews (12): Last reviewed commit: "fix(sendgrid): dedupe active coercion be..." | Re-trigger Greptile

Comment thread apps/sim/blocks/blocks/sendgrid.ts
@waleedlatif1

Copy link
Copy Markdown
Collaborator Author

@greptile review

@waleedlatif1

Copy link
Copy Markdown
Collaborator Author

@cursor review

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ Bugbot reviewed your changes and found no new issues!

Comment @cursor review or bugbot run to trigger another review on this PR

Reviewed by Cursor Bugbot for commit 34d86a2. Configure here.

@waleedlatif1

Copy link
Copy Markdown
Collaborator Author

Fixed the tool-body active coercion Greptile flagged — create_template_version.ts now coerces params.active to 0/1 in its own request body (params.active ? 1 : 0) instead of relying on the block-level fix, so direct tool invocations with a boolean active are also correct. Tightened SendGridTemplateVersionRequest.active to number since it's always coerced now. Pushed in eb0ff54.

@waleedlatif1

Copy link
Copy Markdown
Collaborator Author

@greptile review

@waleedlatif1

Copy link
Copy Markdown
Collaborator Author

@cursor review

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ Bugbot reviewed your changes and found no new issues!

Comment @cursor review or bugbot run to trigger another review on this PR

Reviewed by Cursor Bugbot for commit eb0ff54. Configure here.

@waleedlatif1

Copy link
Copy Markdown
Collaborator Author

Final independent validation pass (3 parallel subagents against live SendGrid docs) caught one more real gap: GET /v3/templates requires page_size on every request (no server-side default) — list_templates was only sending it when the caller explicitly set a value, so an unset call would fail. Now always sends page_size (defaulting to 20, matching the tool's documented default). Pushed in 29f06de.

Everything else across all 16 tools verified aligned with live SendGrid v3 docs, correct nullable output typing, and no backwards-incompatible field removals.

@waleedlatif1

Copy link
Copy Markdown
Collaborator Author

@greptile review

@waleedlatif1

Copy link
Copy Markdown
Collaborator Author

@cursor review

Comment thread apps/sim/tools/sendgrid/create_template_version.ts Outdated
@waleedlatif1

Copy link
Copy Markdown
Collaborator Author

@greptile review

@waleedlatif1

Copy link
Copy Markdown
Collaborator Author

@cursor review

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ Bugbot reviewed your changes and found no new issues!

Comment @cursor review or bugbot run to trigger another review on this PR

Reviewed by Cursor Bugbot for commit c094e47. Configure here.

@waleedlatif1

Copy link
Copy Markdown
Collaborator Author

Confirmed and fixed — real bug: the block coerces active to a number (0/1) before calling the tool, but toActiveFlag only checked for false/'false', so the block's "inactive" selection (numeric 0) fell through to the active branch. Now checks against an explicit inactive-values set covering boolean, string, and numeric forms. Pushed in 2ebda8b.

Re: the nextPageToken block output missing optional: true" — checked the type, block-level OutputFieldDefinition (@sim/workflow-types/blocks) only accepts { type, description?, condition?, hiddenFromDisplay? }, no optional` field exists there (that's a tool-level-only construct). Not applicable, skipping.

@waleedlatif1

Copy link
Copy Markdown
Collaborator Author

@greptile review

@waleedlatif1

Copy link
Copy Markdown
Collaborator Author

@cursor review

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ Bugbot reviewed your changes and found no new issues!

Comment @cursor review or bugbot run to trigger another review on this PR

Reviewed by Cursor Bugbot for commit 2ebda8b. Configure here.

@waleedlatif1

Copy link
Copy Markdown
Collaborator Author

One more adversarial re-verification pass caught a real, pre-existing bug (predates this PR — was Object.assign before, unrelated to anything I'd touched): add_contact.ts merged customFields onto the contact object as top-level sibling keys instead of nesting them under custom_fields. SendGrid's PUT /v3/marketing/contacts requires custom fields nested under a custom_fields object — unrecognized top-level keys are silently dropped, so the documented customFields param never actually reached SendGrid. Fixed to contact.custom_fields = customFields. Pushed in 7671236.

@waleedlatif1

Copy link
Copy Markdown
Collaborator Author

@greptile review

@waleedlatif1

Copy link
Copy Markdown
Collaborator Author

@cursor review

Comment thread apps/sim/tools/sendgrid/list_templates.ts
@waleedlatif1

Copy link
Copy Markdown
Collaborator Author

@greptile review

@waleedlatif1

Copy link
Copy Markdown
Collaborator Author

@cursor review

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ Bugbot reviewed your changes and found no new issues!

Comment @cursor review or bugbot run to trigger another review on this PR

Reviewed by Cursor Bugbot for commit 19bd446. Configure here.

…put typing

- Fix active field for create_template_version being sent as the string
  "true"/"false" instead of the SendGrid-required int 0/1
- Add missing authMode: ApiKey on SendGridBlock
- Add pageToken/nextPageToken pagination support to list_templates and
  list_all_lists (SendGrid page_token cursor, parsed from _metadata.next)
- Fix nullable output fields to use ?? null / ?? [] with optional: true
  across get_contact, search_contacts, remove_contacts_from_list,
  create_template_version, add_contact, send_mail
- Remove dead data.templates fallback in list_templates (API only
  ever returns result)
- Remove unused UpdateContactParams/UpdateListParams/UpdateTemplateParams
  dead types; make CreateTemplateParams.generation optional to match
  actual tool behavior
…e coercion

- Gate listPageToken/templatePageToken remap on operation so a stale
  token from the other list operation can't override the intended one
- Fix active coercion to also treat a real boolean true (from a dynamic
  <Block.output> reference) as active, not just the dropdown string 'true'
Per Greptile: the block-level active coercion only covered the UI
path. A direct sendgrid_create_template_version tool invocation with
a boolean active would still send a raw boolean to SendGrid. Coerce
to 0/1 in the tool's own request body so both paths are correct.
SendGrid's GET /v3/templates requires page_size on every request
(no server-side default) — omitting it errors. Default to 20 to
match our own documented default when the caller doesn't set one.
Per Cursor Bugbot: params.active ? 1 : 0 treated any truthy string
(including "false") as active. Extracted a toActiveFlag helper that
only treats real false or the string 'false' as inactive, everything
else (including unset) defaults to active — matches the tool's
documented default.
Per Greptile: the block coerces active to a number (0/1) before
calling the tool, but toActiveFlag only checked for false/'false',
so the block's inactive selection (0) fell through to the
"active" branch. Check against an explicit inactive-values set
covering the boolean, string, and numeric forms.
Pre-existing bug (predates this PR): custom fields were merged onto
the contact object as top-level sibling keys via safeAssign/Object.assign,
but SendGrid's PUT /v3/marketing/contacts requires them nested under a
custom_fields object. SendGrid silently drops unrecognized top-level
keys, so the documented customFields param never actually reached
SendGrid. Caught during a final adversarial re-verification pass
before merge.
…plates pagination

Per Cursor Bugbot: list_templates always defaults page_size to 20
when unset (required by SendGrid), so a follow-up pageToken-only
call after a first call with a larger pageSize would silently
shrink to 20 and desync page boundaries. This is inherent to a
stateless tool call (SendGrid requires page_size on every request,
and the tool has no way to remember the prior call's value), so
clarify via param description and UI placeholder that callers must
repeat the same pageSize across paginated calls.
Unrelated to the SendGrid work in this branch. staging's own HEAD
already has 884 compliant Zod-backed API routes (0 non-Zod), but this
ratchet baseline was never bumped when that route landed, so any PR
rebasing onto current staging fails check:api-validation:strict with
"route count increased from 883 to 884". All routes remain fully
Zod-backed; this is a mechanical counter update, not a policy change.
@waleedlatif1 waleedlatif1 force-pushed the worktree-sendgrid-validate branch from 19bd446 to 79d3b94 Compare July 2, 2026 17:53
@waleedlatif1

Copy link
Copy Markdown
Collaborator Author

CI failed again after the rebase — but not because of sendgrid. staging had moved forward another 7 commits (sharepoint, supabase, wordpress, clerk, langsmith, vercel), and one of them added a new API route without bumping the check:api-validation:strict ratchet baseline (totalRoutes: 883) to match. Every route is properly Zod-backed (884/884, 0 non-Zod) — it's purely a stale counter, and it would block any PR rebasing onto current staging, not just this one.

Rebased onto latest staging again (7 sendgrid commits, still linear, diff unchanged — verified) and added one small, clearly separate commit bumping the baseline 883→884 to match reality. Confirmed check:api-validation:strict, block-registry check, and typecheck all pass locally now.

@waleedlatif1

Copy link
Copy Markdown
Collaborator Author

@greptile review

@waleedlatif1

Copy link
Copy Markdown
Collaborator Author

@cursor review

Comment thread apps/sim/blocks/blocks/sendgrid.ts Outdated
Per Cursor Bugbot: the block's pre-coercion only recognized the
dropdown string 'true' or boolean true as active, so a dynamic
reference producing numeric 1 or string '1' fell through to 0 and
silently created an inactive template version. Exported the tool's
toActiveFlag and reused it in the block instead of duplicating the
inactive-value logic, so both layers can no longer drift out of sync.
@waleedlatif1

Copy link
Copy Markdown
Collaborator Author

@greptile review

@waleedlatif1

Copy link
Copy Markdown
Collaborator Author

@cursor review

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ Bugbot reviewed your changes and found no new issues!

Comment @cursor review or bugbot run to trigger another review on this PR

Reviewed by Cursor Bugbot for commit 4d3663e. Configure here.

@waleedlatif1 waleedlatif1 merged commit f6b802e into staging Jul 2, 2026
18 checks passed
@waleedlatif1 waleedlatif1 deleted the worktree-sendgrid-validate branch July 2, 2026 18:16
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant