Skip to content

Conversation

@magicspon
Copy link

@magicspon magicspon commented Jan 4, 2026


Summary by cubic

Adds a public Contact Books API with full CRUD so external clients can list, create, read, update, and delete contact books. Updates the OpenAPI spec and docs to expose these endpoints.

  • New Features
    • Endpoints: GET /v1/contactBooks, GET /v1/contactBooks/{contactBookId}, POST /v1/contactBooks, PATCH /v1/contactBooks/{contactBookId}, DELETE /v1/contactBooks/{contactBookId}
    • Shared schema: ContactBookSchema (properties typed, optional _count.contacts)
    • AuthZ: scoped by team API key; returns 403/404 where appropriate
    • Docs: OpenAPI JSON updated and MDX pages added; “Contact Books” group added to API Reference navigation

Written for commit 6f524ab. Summary will update on new commits.

Summary by CodeRabbit

  • New Features
    • Introduced Contact Books API with full CRUD: list, create, retrieve, update, and delete contact books (supports properties, emoji, validation, and related-entity counts).
  • Documentation
    • Added API reference pages for all Contact Books endpoints and integrated the new group into the API Reference navigation.
  • Other
    • Client-side schema and server wiring added to enable Contact Books in the app.

✏️ Tip: You can customize this high-level summary in your review settings.

@vercel
Copy link

vercel bot commented Jan 4, 2026

@magicspon is attempting to deploy a commit to the kmkoushik's projects Team on Vercel.

A member of the Team first needs to authorize it.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Jan 4, 2026

Warning

Rate limit exceeded

@magicspon has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 16 minutes and 33 seconds before requesting another review.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

📥 Commits

Reviewing files that changed from the base of the PR and between 3e9035a and 6f524ab.

📒 Files selected for processing (1)
  • apps/web/src/server/public-api/api/contacts/update-contact-book.ts

Note

Other AI code review bot(s) detected

CodeRabbit has detected other AI code review bot(s) in this pull request and will avoid duplicating their findings in the review comments. This may lead to a less comprehensive review.

Walkthrough

This pull request adds a Contact Books feature: five new HTTP endpoints (GET list, POST create, GET retrieve, PATCH update, DELETE delete) and corresponding route handlers registered in the public API bootstrap. It introduces a Zod ContactBookSchema, service-backed handler implementations that coerce properties to Record<string,string>, updates API reference docs and navigation with five new MDX files, and extends the OpenAPI specification with the new paths. No existing endpoints or functionality are removed or modified.

Pre-merge checks

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'feat: contact books public api' accurately and concisely describes the main change: introducing a new public API for contact books with CRUD operations.

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@cubic-dev-ai cubic-dev-ai bot left a comment

Choose a reason for hiding this comment

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

3 issues found across 14 files

Prompt for AI agents (all issues)

Check if these issues are valid — if so, understand the root cause of each and fix them.


<file name="apps/web/src/server/public-api/api/contact-books/get-contact-book.ts">

<violation number="1" location="apps/web/src/server/public-api/api/contact-books/get-contact-book.ts:30">
P2: The 403 response is documented in the OpenAPI spec but never returned by the implementation. The query filters by both `id` and `teamId`, so when a contact book exists but belongs to another team, it returns 404 instead of 403. Either remove the 403 response definition (if returning 404 for security reasons is intentional) or update the logic to first check existence, then authorization.</violation>
</file>

<file name="apps/web/src/server/public-api/api/contact-books/create-contact-book.ts">

<violation number="1" location="apps/web/src/server/public-api/api/contact-books/create-contact-book.ts:40">
P2: Remove debug `console.log` statement before merging. This will output team data to production logs.</violation>
</file>

<file name="apps/docs/api-reference/contact-books/list-contact-books.mdx">

<violation number="1" location="apps/docs/api-reference/contact-books/list-contact-books.mdx:2">
P2: URL path naming convention inconsistent with existing API endpoints. Existing API documentation uses camelCase (`/v1/contactBooks/...`) for the contact books resource, but this new file uses kebab-case (`/v1/contact-books`). Consider using `/v1/contactBooks` for consistency.</violation>
</file>

Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.

},
description: "Retrieve the contact book",
},
403: {
Copy link

@cubic-dev-ai cubic-dev-ai bot Jan 4, 2026

Choose a reason for hiding this comment

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

P2: The 403 response is documented in the OpenAPI spec but never returned by the implementation. The query filters by both id and teamId, so when a contact book exists but belongs to another team, it returns 404 instead of 403. Either remove the 403 response definition (if returning 404 for security reasons is intentional) or update the logic to first check existence, then authorization.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At apps/web/src/server/public-api/api/contact-books/get-contact-book.ts, line 30:

<comment>The 403 response is documented in the OpenAPI spec but never returned by the implementation. The query filters by both `id` and `teamId`, so when a contact book exists but belongs to another team, it returns 404 instead of 403. Either remove the 403 response definition (if returning 404 for security reasons is intentional) or update the logic to first check existence, then authorization.</comment>

<file context>
@@ -0,0 +1,85 @@
+			},
+			description: &quot;Retrieve the contact book&quot;,
+		},
+		403: {
+			content: {
+				&quot;application/json&quot;: {
</file context>
Fix with Cubic

const team = c.var.team;
const body = c.req.valid("json");

console.log({ team });
Copy link

@cubic-dev-ai cubic-dev-ai bot Jan 4, 2026

Choose a reason for hiding this comment

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

P2: Remove debug console.log statement before merging. This will output team data to production logs.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At apps/web/src/server/public-api/api/contact-books/create-contact-book.ts, line 40:

<comment>Remove debug `console.log` statement before merging. This will output team data to production logs.</comment>

<file context>
@@ -0,0 +1,67 @@
+		const team = c.var.team;
+		const body = c.req.valid(&quot;json&quot;);
+
+		console.log({ team });
+
+		const contactBook = await createContactBookService(team.id, body.name);
</file context>

✅ Addressed in 8a8be80

@@ -0,0 +1,3 @@
---
openapi: get /v1/contact-books
Copy link

@cubic-dev-ai cubic-dev-ai bot Jan 4, 2026

Choose a reason for hiding this comment

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

P2: URL path naming convention inconsistent with existing API endpoints. Existing API documentation uses camelCase (/v1/contactBooks/...) for the contact books resource, but this new file uses kebab-case (/v1/contact-books). Consider using /v1/contactBooks for consistency.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At apps/docs/api-reference/contact-books/list-contact-books.mdx, line 2:

<comment>URL path naming convention inconsistent with existing API endpoints. Existing API documentation uses camelCase (`/v1/contactBooks/...`) for the contact books resource, but this new file uses kebab-case (`/v1/contact-books`). Consider using `/v1/contactBooks` for consistency.</comment>

<file context>
@@ -0,0 +1,3 @@
+---
+openapi: get /v1/contact-books
+---
</file context>

✅ Addressed in 3a5a8df

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 3

📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between bba9e93 and c87b587.

📒 Files selected for processing (14)
  • apps/docs/api-reference/contact-books/create-contact-book.mdx
  • apps/docs/api-reference/contact-books/delete-contact-book.mdx
  • apps/docs/api-reference/contact-books/get-contact-book.mdx
  • apps/docs/api-reference/contact-books/list-contact-books.mdx
  • apps/docs/api-reference/contact-books/update-contact-book.mdx
  • apps/docs/api-reference/openapi.json
  • apps/docs/docs.json
  • apps/web/src/lib/zod/contact-book-schema.ts
  • apps/web/src/server/public-api/api/contact-books/create-contact-book.ts
  • apps/web/src/server/public-api/api/contact-books/delete-contact-book.ts
  • apps/web/src/server/public-api/api/contact-books/get-contact-book.ts
  • apps/web/src/server/public-api/api/contact-books/get-contact-books.ts
  • apps/web/src/server/public-api/api/contact-books/update-contact-book.ts
  • apps/web/src/server/public-api/index.ts
🧰 Additional context used
📓 Path-based instructions (5)
**/*.{tsx,ts,jsx,js}

📄 CodeRabbit inference engine (.cursor/rules/general.mdc)

Include all required imports and ensure proper naming of key components in React/NextJS code

Files:

  • apps/web/src/server/public-api/api/contact-books/get-contact-books.ts
  • apps/web/src/server/public-api/api/contact-books/create-contact-book.ts
  • apps/web/src/lib/zod/contact-book-schema.ts
  • apps/web/src/server/public-api/api/contact-books/update-contact-book.ts
  • apps/web/src/server/public-api/api/contact-books/delete-contact-book.ts
  • apps/web/src/server/public-api/index.ts
  • apps/web/src/server/public-api/api/contact-books/get-contact-book.ts
**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{ts,tsx,js,jsx}: Use TypeScript-first approach with 2-space indent and semicolons enabled by Prettier in apps/web (Next.js), apps/marketing, apps/smtp-server, and all packages
Never use dynamic imports; always import on the top level
Run ESLint via @usesend/eslint-config and ensure no warnings remain before submitting PRs

Files:

  • apps/web/src/server/public-api/api/contact-books/get-contact-books.ts
  • apps/web/src/server/public-api/api/contact-books/create-contact-book.ts
  • apps/web/src/lib/zod/contact-book-schema.ts
  • apps/web/src/server/public-api/api/contact-books/update-contact-book.ts
  • apps/web/src/server/public-api/api/contact-books/delete-contact-book.ts
  • apps/web/src/server/public-api/index.ts
  • apps/web/src/server/public-api/api/contact-books/get-contact-book.ts
apps/web/**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (AGENTS.md)

Use alias ~/ for src imports in apps/web (e.g., import { x } from "~/utils/x")

Files:

  • apps/web/src/server/public-api/api/contact-books/get-contact-books.ts
  • apps/web/src/server/public-api/api/contact-books/create-contact-book.ts
  • apps/web/src/lib/zod/contact-book-schema.ts
  • apps/web/src/server/public-api/api/contact-books/update-contact-book.ts
  • apps/web/src/server/public-api/api/contact-books/delete-contact-book.ts
  • apps/web/src/server/public-api/index.ts
  • apps/web/src/server/public-api/api/contact-books/get-contact-book.ts
apps/web/**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

apps/web/**/*.{ts,tsx}: Prefer to use TRPC for client-server communication unless explicitly asked otherwise in apps/web
Use Prisma for database access in apps/web

Files:

  • apps/web/src/server/public-api/api/contact-books/get-contact-books.ts
  • apps/web/src/server/public-api/api/contact-books/create-contact-book.ts
  • apps/web/src/lib/zod/contact-book-schema.ts
  • apps/web/src/server/public-api/api/contact-books/update-contact-book.ts
  • apps/web/src/server/public-api/api/contact-books/delete-contact-book.ts
  • apps/web/src/server/public-api/index.ts
  • apps/web/src/server/public-api/api/contact-books/get-contact-book.ts
**/*.{ts,tsx,md}

📄 CodeRabbit inference engine (AGENTS.md)

Run Prettier 3 for code formatting on TypeScript, TSX, and Markdown files

Files:

  • apps/web/src/server/public-api/api/contact-books/get-contact-books.ts
  • apps/web/src/server/public-api/api/contact-books/create-contact-book.ts
  • apps/web/src/lib/zod/contact-book-schema.ts
  • apps/web/src/server/public-api/api/contact-books/update-contact-book.ts
  • apps/web/src/server/public-api/api/contact-books/delete-contact-book.ts
  • apps/web/src/server/public-api/index.ts
  • apps/web/src/server/public-api/api/contact-books/get-contact-book.ts
🧬 Code graph analysis (5)
apps/web/src/server/public-api/api/contact-books/get-contact-books.ts (4)
apps/web/src/lib/zod/contact-book-schema.ts (1)
  • ContactBookSchema (3-23)
apps/web/src/server/service/contact-book-service.ts (1)
  • getContactBooks (6-18)
apps/web/src/server/public-api/index.ts (1)
  • app (30-30)
apps/web/src/server/public-api/hono.ts (1)
  • PublicAPIApp (136-136)
apps/web/src/server/public-api/api/contact-books/create-contact-book.ts (4)
apps/web/src/lib/zod/contact-book-schema.ts (1)
  • ContactBookSchema (3-23)
apps/web/src/server/service/contact-book-service.ts (2)
  • createContactBook (20-40)
  • updateContactBook (69-81)
apps/web/src/server/public-api/index.ts (1)
  • app (30-30)
apps/web/src/server/public-api/hono.ts (1)
  • PublicAPIApp (136-136)
apps/web/src/server/public-api/api/contact-books/update-contact-book.ts (6)
apps/web/src/lib/zod/contact-book-schema.ts (1)
  • ContactBookSchema (3-23)
apps/web/src/server/service/contact-book-service.ts (1)
  • updateContactBook (69-81)
apps/web/src/server/public-api/index.ts (1)
  • app (30-30)
apps/web/src/server/public-api/hono.ts (1)
  • PublicAPIApp (136-136)
apps/web/src/server/db.ts (1)
  • db (20-20)
apps/web/src/server/public-api/api-error.ts (1)
  • UnsendApiError (62-75)
apps/web/src/server/public-api/api/contact-books/delete-contact-book.ts (5)
apps/web/src/server/service/contact-book-service.ts (1)
  • deleteContactBook (83-87)
apps/web/src/server/public-api/index.ts (1)
  • app (30-30)
apps/web/src/server/public-api/hono.ts (1)
  • PublicAPIApp (136-136)
apps/web/src/server/db.ts (1)
  • db (20-20)
apps/web/src/server/public-api/api-error.ts (1)
  • UnsendApiError (62-75)
apps/web/src/server/public-api/index.ts (2)
apps/web/src/server/service/contact-book-service.ts (4)
  • getContactBooks (6-18)
  • createContactBook (20-40)
  • updateContactBook (69-81)
  • deleteContactBook (83-87)
apps/web/src/server/public-api/api-utils.ts (1)
  • getContactBook (5-27)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: cubic · AI code reviewer
🔇 Additional comments (13)
apps/docs/api-reference/contact-books/update-contact-book.mdx (1)

1-3: LGTM! Documentation structure is correct.

The MDX file correctly references the PATCH operation for updating contact books. The structure follows the expected pattern for OpenAPI-based documentation.

apps/docs/api-reference/contact-books/list-contact-books.mdx (1)

1-3: LGTM! Documentation structure is correct.

The MDX file correctly references the GET operation for listing contact books. The structure is consistent with the other Contact Books API documentation files.

apps/docs/api-reference/contact-books/create-contact-book.mdx (1)

1-3: LGTM! Documentation structure is correct.

The MDX file correctly references the POST operation for creating contact books. The structure follows the expected pattern.

apps/docs/api-reference/contact-books/delete-contact-book.mdx (1)

1-3: LGTM! Documentation structure is correct.

The MDX file correctly references the DELETE operation for removing contact books. The structure is consistent with the other endpoint documentation files.

apps/docs/api-reference/contact-books/get-contact-book.mdx (1)

1-3: LGTM! Verification confirms consistency across OpenAPI spec, MDX docs, and route implementations.

All five contact-books operations are properly defined and consistently implemented:

  • GET /v1/contact-books (list)
  • POST /v1/contact-books (create)
  • GET /v1/contact-books/{id} (the reviewed file - get single)
  • PATCH /v1/contact-books/{id} (update)
  • DELETE /v1/contact-books/{id} (delete)

The MDX file correctly references the operation, and all routes are properly registered in the public API.

apps/web/src/server/public-api/index.ts (2)

24-28: LGTM!

The contact book endpoint imports follow the established pattern and are correctly structured.


55-60: LGTM!

The contact book API registration follows the existing pattern and is properly organized with a descriptive comment header.

apps/docs/docs.json (1)

68-76: LGTM!

The new Contact Books API documentation group is well-structured and consistent with existing API reference groups.

apps/web/src/server/public-api/api/contact-books/get-contact-books.ts (1)

1-37: LGTM!

The implementation is clean and follows best practices. The properties sanitization ensures type safety for the API response.

apps/web/src/lib/zod/contact-book-schema.ts (1)

1-23: LGTM! Well-structured schema with comprehensive OpenAPI metadata.

The ContactBookSchema is properly defined with clear descriptions and examples for all fields. The optional _count field appropriately handles cases where contact counts may not be included in the response.

apps/web/src/server/public-api/api/contact-books/delete-contact-book.ts (1)

62-76: Check-then-delete pattern is acceptable but slightly redundant.

The preliminary existence check (lines 62-74) provides a clear NOT_FOUND error before calling the delete service. While the delete operation itself would fail if the record doesn't exist, the explicit check enables a more specific error message and consistent error handling across endpoints.

This pattern is acceptable for API endpoints, though technically the check could be omitted if the service layer returned a more specific error.

apps/docs/api-reference/openapi.json (1)

1698-2036: LGTM! Comprehensive and well-structured OpenAPI definitions for Contact Books API.

The new endpoints provide complete CRUD operations with:

  • Consistent schema definitions across all operations
  • Proper error responses (403 Forbidden, 404 Not Found)
  • Appropriate inclusion of _count metadata in GET responses
  • Clear descriptions and examples
  • Correct required field annotations

The OpenAPI spec aligns well with the implementation and provides clear documentation for API consumers.

apps/web/src/server/public-api/api/contact-books/get-contact-book.ts (1)

54-83: The type cast is necessary but reveals a runtime safety concern.

The properties field is defined as Json in the Prisma schema, which Prisma resolves to an untyped value. The explicit cast to Record<string, string> is required for TypeScript compilation. However, this cast assumes the stored JSON matches the expected structure without runtime validation. Consider either:

  • Adding runtime validation to ensure the JSON structure matches Record<string, string>, or
  • Documenting the assumption that properties must conform to this shape

Copy link

@cubic-dev-ai cubic-dev-ai bot left a comment

Choose a reason for hiding this comment

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

2 issues found across 3 files (changes from recent commits).

Prompt for AI agents (all issues)

Check if these issues are valid — if so, understand the root cause of each and fix them.


<file name="apps/web/src/server/public-api/api/contact-books/delete-contact-book.ts">

<violation number="1" location="apps/web/src/server/public-api/api/contact-books/delete-contact-book.ts:61">
P0: Parameter name mismatch: `getContactBook` expects a path param named `contactBookId`, but this route uses `id`. This will cause all delete requests to fail with &quot;contactBookId is mandatory&quot; error. Either change the route param to `contactBookId` or pass the `contactBookId` explicitly to a modified utility function.</violation>
</file>

<file name="apps/web/src/server/public-api/api/contact-books/update-contact-book.ts">

<violation number="1" location="apps/web/src/server/public-api/api/contact-books/update-contact-book.ts:72">
P1: The `getContactBook` utility expects a route param named `contactBookId`, but this route uses `{id}`. This will always fail with &quot;contactBookId is mandatory&quot;. Either rename the route param to `{contactBookId}` or revert to the original inline check that uses `c.req.valid(&quot;param&quot;).id`.</violation>
</file>

Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.

const team = c.var.team;
const contactBookId = c.req.valid("param").id;

await getContactBook(c, team.id);
Copy link

@cubic-dev-ai cubic-dev-ai bot Jan 4, 2026

Choose a reason for hiding this comment

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

P0: Parameter name mismatch: getContactBook expects a path param named contactBookId, but this route uses id. This will cause all delete requests to fail with "contactBookId is mandatory" error. Either change the route param to contactBookId or pass the contactBookId explicitly to a modified utility function.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At apps/web/src/server/public-api/api/contact-books/delete-contact-book.ts, line 61:

<comment>Parameter name mismatch: `getContactBook` expects a path param named `contactBookId`, but this route uses `id`. This will cause all delete requests to fail with &quot;contactBookId is mandatory&quot; error. Either change the route param to `contactBookId` or pass the `contactBookId` explicitly to a modified utility function.</comment>

<file context>
@@ -59,19 +58,7 @@ function deleteContactBook(app: PublicAPIApp) {
-				message: &quot;Contact book not found&quot;,
-			});
-		}
+		await getContactBook(c, team.id);
 
 		const deletedContactBook = await deleteContactBookService(contactBookId);
</file context>

✅ Addressed in 3a5a8df

const contactBookId = c.req.valid("param").id;
const body = c.req.valid("json");

await getContactBook(c, team.id);
Copy link

@cubic-dev-ai cubic-dev-ai bot Jan 4, 2026

Choose a reason for hiding this comment

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

P1: The getContactBook utility expects a route param named contactBookId, but this route uses {id}. This will always fail with "contactBookId is mandatory". Either rename the route param to {contactBookId} or revert to the original inline check that uses c.req.valid("param").id.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At apps/web/src/server/public-api/api/contact-books/update-contact-book.ts, line 72:

<comment>The `getContactBook` utility expects a route param named `contactBookId`, but this route uses `{id}`. This will always fail with &quot;contactBookId is mandatory&quot;. Either rename the route param to `{contactBookId}` or revert to the original inline check that uses `c.req.valid(&quot;param&quot;).id`.</comment>

<file context>
@@ -70,19 +69,7 @@ function updateContactBook(app: PublicAPIApp) {
-				message: &quot;Contact book not found&quot;,
-			});
-		}
+		await getContactBook(c, team.id);
 
 		const updated = await updateContactBookService(contactBookId, body);
</file context>

✅ Addressed in 3a5a8df

Copy link

@cubic-dev-ai cubic-dev-ai bot left a comment

Choose a reason for hiding this comment

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

1 issue found across 18 files (changes from recent commits).

Prompt for AI agents (all issues)

Check if these issues are valid — if so, understand the root cause of each and fix them.


<file name="apps/web/src/server/public-api/api/contacts/update-contact-book.ts">

<violation number="1" location="apps/web/src/server/public-api/api/contacts/update-contact-book.ts:74">
P2: Remove debug `console.log` statement before merging. This appears to be leftover debugging code that shouldn&#39;t be in a production API endpoint.</violation>
</file>

Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.


await getContactBook(c, team.id);

console.log({ contactBookId });
Copy link

@cubic-dev-ai cubic-dev-ai bot Jan 4, 2026

Choose a reason for hiding this comment

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

P2: Remove debug console.log statement before merging. This appears to be leftover debugging code that shouldn't be in a production API endpoint.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At apps/web/src/server/public-api/api/contacts/update-contact-book.ts, line 74:

<comment>Remove debug `console.log` statement before merging. This appears to be leftover debugging code that shouldn&#39;t be in a production API endpoint.</comment>

<file context>
@@ -66,11 +66,13 @@ const route = createRoute({
 
 		await getContactBook(c, team.id);
 
+		console.log({ contactBookId });
+
 		const updated = await updateContactBookService(contactBookId, body);
</file context>

✅ Addressed in b3306e9

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

♻️ Duplicate comments (2)
apps/web/src/server/public-api/api/contacts/update-contact-book.ts (1)

72-72: Past review comment appears resolved.

The previous comment raised concerns about a parameter name mismatch, but the current code correctly uses {contactBookId} throughout (route path on line 9, param schema on line 12, and param extraction on line 69), which matches the getContactBook utility's expectations.

apps/web/src/server/public-api/api/contacts/get-contact-book.ts (1)

30-40: Remove 403 response from OpenAPI spec or implement authorization check.

The 403 response is documented but never returned by the implementation. The query on lines 59-63 filters by both id and teamId simultaneously, so both "resource doesn't exist" and "resource exists but belongs to another team" scenarios return 404.

Returning 404 in both cases is actually a security best practice (prevents information disclosure about resource existence). However, the OpenAPI specification should accurately reflect the implementation.

🔎 Recommended fix: Remove the 403 response definition
 		},
-		403: {
-			content: {
-				"application/json": {
-					schema: z.object({
-						error: z.string(),
-					}),
-				},
-			},
-			description:
-				"Forbidden - API key doesn't have access to this contact book",
-		},
🧹 Nitpick comments (3)
apps/web/src/server/public-api/api/contacts/delete-contact-book.ts (1)

56-71: Consider using the return value from getContactBook to avoid redundant parameter extraction.

Line 59 extracts contactBookId from the validated params, then line 61 calls getContactBook which internally extracts the same parameter again via c.req.param("contactBookId"). The returned contact book is discarded, and contactBookId is re-used on line 63.

🔎 Proposed refactor to eliminate redundancy
 function deleteContactBook(app: PublicAPIApp) {
 	app.openapi(route, async (c) => {
 		const team = c.var.team;
-		const contactBookId = c.req.valid("param").contactBookId;
 
-		await getContactBook(c, team.id);
+		const contactBook = await getContactBook(c, team.id);
 
-		const deletedContactBook = await deleteContactBookService(contactBookId);
+		const deletedContactBook = await deleteContactBookService(contactBook.id);
 
 		return c.json({
 			id: deletedContactBook.id,
apps/web/src/server/public-api/api/contacts/get-contact-books.ts (1)

6-19: Consider exposing search query parameter.

The underlying service function getContactBooksService supports an optional search parameter for filtering contact books by name (see apps/web/src/server/service/contact-book-service.ts lines 5-17), but the route definition doesn't expose this capability. Consider adding a query parameter to enable search functionality.

🔎 Proposed enhancement to add search support
 const route = createRoute({
 	method: "get",
 	path: "/v1/contactBooks",
+	request: {
+		query: z.object({
+			search: z.string().optional().openapi({
+				description: "Search contact books by name",
+				example: "Newsletter"
+			}),
+		}),
+	},
 	responses: {

Then update the handler:

 function getContactBooks(app: PublicAPIApp) {
 	app.openapi(route, async (c) => {
 		const team = c.var.team;
+		const { search } = c.req.valid("query");
 
-		const contactBooks = await getContactBooksService(team.id);
+		const contactBooks = await getContactBooksService(team.id, search);
apps/web/src/server/public-api/api/contacts/create-contact-book.ts (1)

38-63: Consider refactoring to reduce database calls.

The current implementation creates the contact book (line 43), then conditionally updates it with emoji/properties (lines 46-50), resulting in two database operations when emoji or properties are provided.

While this works correctly, it could be optimized by enhancing createContactBookService to accept optional emoji and properties parameters, allowing everything to be created in a single database operation.

💡 Suggested refactor (requires service function update)

Update the service function signature at apps/web/src/server/service/contact-book-service.ts:

export async function createContactBook(
  teamId: number, 
  name: string,
  emoji?: string,
  properties?: Record<string, string>
)

Then simplify the handler:

 function createContactBook(app: PublicAPIApp) {
 	app.openapi(route, async (c) => {
 		const team = c.var.team;
 		const body = c.req.valid("json");
 
-		const contactBook = await createContactBookService(team.id, body.name);
-
-		// Update emoji and properties if provided
-		if (body.emoji || body.properties) {
-			const updated = await updateContactBook(contactBook.id, {
-				emoji: body.emoji,
-				properties: body.properties,
-			});
-
-			return c.json({
-				...updated,
-				properties: updated.properties as Record<string, string>,
-			});
-		}
+		const contactBook = await createContactBookService(
+			team.id, 
+			body.name,
+			body.emoji,
+			body.properties
+		);
 
 		return c.json({
 			...contactBook,
📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between c87b587 and 9d55bab.

📒 Files selected for processing (13)
  • apps/docs/api-reference/contacts/create-contact-book.mdx
  • apps/docs/api-reference/contacts/delete-contact-book.mdx
  • apps/docs/api-reference/contacts/get-contact-book.mdx
  • apps/docs/api-reference/contacts/list-contact-books.mdx
  • apps/docs/api-reference/contacts/update-contact-book.mdx
  • apps/docs/api-reference/openapi.json
  • apps/docs/docs.json
  • apps/web/src/server/public-api/api/contacts/create-contact-book.ts
  • apps/web/src/server/public-api/api/contacts/delete-contact-book.ts
  • apps/web/src/server/public-api/api/contacts/get-contact-book.ts
  • apps/web/src/server/public-api/api/contacts/get-contact-books.ts
  • apps/web/src/server/public-api/api/contacts/update-contact-book.ts
  • apps/web/src/server/public-api/index.ts
✅ Files skipped from review due to trivial changes (3)
  • apps/docs/api-reference/contacts/delete-contact-book.mdx
  • apps/docs/api-reference/contacts/update-contact-book.mdx
  • apps/docs/api-reference/contacts/list-contact-books.mdx
🧰 Additional context used
📓 Path-based instructions (5)
**/*.{tsx,ts,jsx,js}

📄 CodeRabbit inference engine (.cursor/rules/general.mdc)

Include all required imports and ensure proper naming of key components in React/NextJS code

Files:

  • apps/web/src/server/public-api/api/contacts/get-contact-books.ts
  • apps/web/src/server/public-api/api/contacts/delete-contact-book.ts
  • apps/web/src/server/public-api/api/contacts/get-contact-book.ts
  • apps/web/src/server/public-api/api/contacts/create-contact-book.ts
  • apps/web/src/server/public-api/index.ts
  • apps/web/src/server/public-api/api/contacts/update-contact-book.ts
**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{ts,tsx,js,jsx}: Use TypeScript-first approach with 2-space indent and semicolons enabled by Prettier in apps/web (Next.js), apps/marketing, apps/smtp-server, and all packages
Never use dynamic imports; always import on the top level
Run ESLint via @usesend/eslint-config and ensure no warnings remain before submitting PRs

Files:

  • apps/web/src/server/public-api/api/contacts/get-contact-books.ts
  • apps/web/src/server/public-api/api/contacts/delete-contact-book.ts
  • apps/web/src/server/public-api/api/contacts/get-contact-book.ts
  • apps/web/src/server/public-api/api/contacts/create-contact-book.ts
  • apps/web/src/server/public-api/index.ts
  • apps/web/src/server/public-api/api/contacts/update-contact-book.ts
apps/web/**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (AGENTS.md)

Use alias ~/ for src imports in apps/web (e.g., import { x } from "~/utils/x")

Files:

  • apps/web/src/server/public-api/api/contacts/get-contact-books.ts
  • apps/web/src/server/public-api/api/contacts/delete-contact-book.ts
  • apps/web/src/server/public-api/api/contacts/get-contact-book.ts
  • apps/web/src/server/public-api/api/contacts/create-contact-book.ts
  • apps/web/src/server/public-api/index.ts
  • apps/web/src/server/public-api/api/contacts/update-contact-book.ts
apps/web/**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

apps/web/**/*.{ts,tsx}: Prefer to use TRPC for client-server communication unless explicitly asked otherwise in apps/web
Use Prisma for database access in apps/web

Files:

  • apps/web/src/server/public-api/api/contacts/get-contact-books.ts
  • apps/web/src/server/public-api/api/contacts/delete-contact-book.ts
  • apps/web/src/server/public-api/api/contacts/get-contact-book.ts
  • apps/web/src/server/public-api/api/contacts/create-contact-book.ts
  • apps/web/src/server/public-api/index.ts
  • apps/web/src/server/public-api/api/contacts/update-contact-book.ts
**/*.{ts,tsx,md}

📄 CodeRabbit inference engine (AGENTS.md)

Run Prettier 3 for code formatting on TypeScript, TSX, and Markdown files

Files:

  • apps/web/src/server/public-api/api/contacts/get-contact-books.ts
  • apps/web/src/server/public-api/api/contacts/delete-contact-book.ts
  • apps/web/src/server/public-api/api/contacts/get-contact-book.ts
  • apps/web/src/server/public-api/api/contacts/create-contact-book.ts
  • apps/web/src/server/public-api/index.ts
  • apps/web/src/server/public-api/api/contacts/update-contact-book.ts
🧠 Learnings (1)
📚 Learning: 2025-11-28T21:14:07.734Z
Learnt from: CR
Repo: usesend/useSend PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-11-28T21:14:07.734Z
Learning: Applies to **/*.{ts,tsx,js,jsx} : Never use dynamic imports; always import on the top level

Applied to files:

  • apps/web/src/server/public-api/api/contacts/create-contact-book.ts
🧬 Code graph analysis (5)
apps/web/src/server/public-api/api/contacts/get-contact-books.ts (4)
apps/web/src/lib/zod/contact-book-schema.ts (1)
  • ContactBookSchema (3-23)
apps/web/src/server/service/contact-book-service.ts (1)
  • getContactBooks (6-18)
apps/web/src/server/public-api/index.ts (1)
  • app (30-30)
apps/web/src/server/public-api/hono.ts (1)
  • PublicAPIApp (136-136)
apps/web/src/server/public-api/api/contacts/get-contact-book.ts (6)
apps/web/src/lib/zod/contact-book-schema.ts (1)
  • ContactBookSchema (3-23)
apps/web/src/server/public-api/api-utils.ts (1)
  • getContactBook (5-27)
apps/web/src/server/public-api/index.ts (1)
  • app (30-30)
apps/web/src/server/public-api/hono.ts (1)
  • PublicAPIApp (136-136)
apps/web/src/server/db.ts (1)
  • db (20-20)
apps/web/src/server/public-api/api-error.ts (1)
  • UnsendApiError (62-75)
apps/web/src/server/public-api/api/contacts/create-contact-book.ts (3)
apps/web/src/lib/zod/contact-book-schema.ts (1)
  • ContactBookSchema (3-23)
apps/web/src/server/service/contact-book-service.ts (2)
  • createContactBook (20-40)
  • updateContactBook (69-81)
apps/web/src/server/public-api/hono.ts (1)
  • PublicAPIApp (136-136)
apps/web/src/server/public-api/index.ts (2)
apps/web/src/server/service/contact-book-service.ts (4)
  • getContactBooks (6-18)
  • createContactBook (20-40)
  • updateContactBook (69-81)
  • deleteContactBook (83-87)
apps/web/src/server/public-api/api-utils.ts (1)
  • getContactBook (5-27)
apps/web/src/server/public-api/api/contacts/update-contact-book.ts (5)
apps/web/src/lib/zod/contact-book-schema.ts (1)
  • ContactBookSchema (3-23)
apps/web/src/server/service/contact-book-service.ts (1)
  • updateContactBook (69-81)
apps/web/src/server/public-api/index.ts (1)
  • app (30-30)
apps/web/src/server/public-api/hono.ts (1)
  • PublicAPIApp (136-136)
apps/web/src/server/public-api/api-utils.ts (1)
  • getContactBook (5-27)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: cubic · AI code reviewer
🔇 Additional comments (11)
apps/docs/api-reference/contacts/create-contact-book.mdx (1)

1-3: LGTM!

The MDX front matter correctly references the OpenAPI operation. Note: the path convention (/v1/contactBooks) has been flagged for verification in the main implementation file.

apps/docs/api-reference/contacts/get-contact-book.mdx (1)

1-3: LGTM!

The MDX front matter correctly references the GET operation for a single contact book. Path convention has been flagged for verification in the implementation file.

apps/web/src/server/public-api/index.ts (2)

24-28: LGTM!

The imports for the five Contact Book endpoints follow the established pattern and are correctly organized.


55-60: LGTM!

The Contact Book API registration follows the established pattern of other API groups. The endpoints are registered in a logical order (list, create, get, update, delete) and the section comment is consistent with other sections.

apps/docs/docs.json (1)

68-76: LGTM!

The new "Contact Books" group is well-positioned in the navigation structure and includes all five CRUD operations in a logical order. The naming and structure are consistent with other API Reference groups.

apps/web/src/server/public-api/api/contacts/update-contact-book.ts (1)

7-19: Verify API path naming convention with PR description.

The PR description states the endpoint should be PATCH /v1/contact-books/{id}, but the implementation uses PATCH /v1/contactBooks/{contactBookId}.

While the current implementation is consistent with all other contact book endpoints (get-contact-book, delete-contact-book, create-contact-book), there's an inconsistency across the API: domains and emails endpoints use kebab-case paths (/v1/domains, /v1/emails), while all contact endpoints use camelCase (/v1/contactBooks).

Clarify which convention is correct:

  • If /v1/contact-books/{id} is the intended design, update this route and all other contact endpoints to kebab-case
  • If /v1/contactBooks/{contactBookId} is correct, update the PR description
apps/web/src/server/public-api/api/contacts/delete-contact-book.ts (2)

6-54: LGTM!

The route definition is well-structured with proper OpenAPI metadata and response schemas for all expected status codes.


61-61: Past review comment is no longer valid.

The previous concern about parameter name mismatch between the route and getContactBook utility has been resolved. The route correctly uses contactBookId as the path parameter (line 8), which matches what the getContactBook utility expects when extracting via c.req.param("contactBookId").

apps/web/src/server/public-api/api/contacts/get-contact-books.ts (1)

21-35: LGTM!

The handler correctly fetches contact books scoped to the team and sanitizes the properties field for type compatibility between Prisma's JsonValue and the schema's Record<string, string>.

apps/web/src/server/public-api/api/contacts/get-contact-book.ts (1)

78-81: LGTM!

The response correctly casts the properties field for type compatibility with the ContactBookSchema.

apps/web/src/server/public-api/api/contacts/create-contact-book.ts (1)

1-36: LGTM! Dynamic import issue resolved.

All imports are now at the top level, complying with the coding guideline: "Never use dynamic imports; always import on the top level". The route definition is properly structured with appropriate request/response schemas.

As per coding guidelines.

…ontact-book.ts

Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 9d55bab and b3306e9.

📒 Files selected for processing (1)
  • apps/web/src/server/public-api/api/contacts/update-contact-book.ts
🧰 Additional context used
📓 Path-based instructions (5)
**/*.{tsx,ts,jsx,js}

📄 CodeRabbit inference engine (.cursor/rules/general.mdc)

Include all required imports and ensure proper naming of key components in React/NextJS code

Files:

  • apps/web/src/server/public-api/api/contacts/update-contact-book.ts
**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{ts,tsx,js,jsx}: Use TypeScript-first approach with 2-space indent and semicolons enabled by Prettier in apps/web (Next.js), apps/marketing, apps/smtp-server, and all packages
Never use dynamic imports; always import on the top level
Run ESLint via @usesend/eslint-config and ensure no warnings remain before submitting PRs

Files:

  • apps/web/src/server/public-api/api/contacts/update-contact-book.ts
apps/web/**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (AGENTS.md)

Use alias ~/ for src imports in apps/web (e.g., import { x } from "~/utils/x")

Files:

  • apps/web/src/server/public-api/api/contacts/update-contact-book.ts
apps/web/**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

apps/web/**/*.{ts,tsx}: Prefer to use TRPC for client-server communication unless explicitly asked otherwise in apps/web
Use Prisma for database access in apps/web

Files:

  • apps/web/src/server/public-api/api/contacts/update-contact-book.ts
**/*.{ts,tsx,md}

📄 CodeRabbit inference engine (AGENTS.md)

Run Prettier 3 for code formatting on TypeScript, TSX, and Markdown files

Files:

  • apps/web/src/server/public-api/api/contacts/update-contact-book.ts
🧬 Code graph analysis (1)
apps/web/src/server/public-api/api/contacts/update-contact-book.ts (5)
apps/web/src/lib/zod/contact-book-schema.ts (1)
  • ContactBookSchema (3-23)
apps/web/src/server/service/contact-book-service.ts (1)
  • updateContactBook (69-81)
apps/web/src/server/public-api/index.ts (1)
  • app (30-30)
apps/web/src/server/public-api/hono.ts (1)
  • PublicAPIApp (136-136)
apps/web/src/server/public-api/api-utils.ts (1)
  • getContactBook (5-27)
🪛 Biome (2.1.2)
apps/web/src/server/public-api/api/contacts/update-contact-book.ts

[error] 76-76: Shouldn't redeclare 'updated'. Consider to delete it or rename it.

'updated' is defined here:

(lint/suspicious/noRedeclare)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: cubic · AI code reviewer
🔇 Additional comments (2)
apps/web/src/server/public-api/api/contacts/update-contact-book.ts (2)

1-64: Route definition looks well-structured.

The imports follow coding guidelines using the ~/ alias, and the OpenAPI route definition properly validates params, request body, and defines comprehensive response schemas for success and error cases.


78-85: Response transformation and export are correct.

The handler properly transforms the updated contact book with properties coerced to Record<string, string> to match the schema, and the default export follows the expected pattern for route registration.

magicspon and others added 2 commits January 4, 2026 19:23
…t-book.ts

Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between b3306e9 and 3e9035a.

📒 Files selected for processing (1)
  • apps/web/src/server/public-api/api/contacts/update-contact-book.ts
🧰 Additional context used
📓 Path-based instructions (5)
**/*.{tsx,ts,jsx,js}

📄 CodeRabbit inference engine (.cursor/rules/general.mdc)

Include all required imports and ensure proper naming of key components in React/NextJS code

Files:

  • apps/web/src/server/public-api/api/contacts/update-contact-book.ts
**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{ts,tsx,js,jsx}: Use TypeScript-first approach with 2-space indent and semicolons enabled by Prettier in apps/web (Next.js), apps/marketing, apps/smtp-server, and all packages
Never use dynamic imports; always import on the top level
Run ESLint via @usesend/eslint-config and ensure no warnings remain before submitting PRs

Files:

  • apps/web/src/server/public-api/api/contacts/update-contact-book.ts
apps/web/**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (AGENTS.md)

Use alias ~/ for src imports in apps/web (e.g., import { x } from "~/utils/x")

Files:

  • apps/web/src/server/public-api/api/contacts/update-contact-book.ts
apps/web/**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

apps/web/**/*.{ts,tsx}: Prefer to use TRPC for client-server communication unless explicitly asked otherwise in apps/web
Use Prisma for database access in apps/web

Files:

  • apps/web/src/server/public-api/api/contacts/update-contact-book.ts
**/*.{ts,tsx,md}

📄 CodeRabbit inference engine (AGENTS.md)

Run Prettier 3 for code formatting on TypeScript, TSX, and Markdown files

Files:

  • apps/web/src/server/public-api/api/contacts/update-contact-book.ts
🧬 Code graph analysis (1)
apps/web/src/server/public-api/api/contacts/update-contact-book.ts (4)
apps/web/src/lib/zod/contact-book-schema.ts (1)
  • ContactBookSchema (3-23)
apps/web/src/server/service/contact-book-service.ts (1)
  • updateContactBook (69-81)
apps/web/src/server/public-api/hono.ts (1)
  • PublicAPIApp (136-136)
apps/web/src/server/public-api/api-utils.ts (1)
  • getContactBook (5-27)
🪛 Biome (2.1.2)
apps/web/src/server/public-api/api/contacts/update-contact-book.ts

[error] 78-78: expected : but instead found c

Remove c

(parse)


[error] 81-81: expected , but instead found ;

Remove ;

(parse)

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