Skip to content

Conversation

@michelle0927
Copy link
Collaborator

@michelle0927 michelle0927 commented Aug 25, 2025

Resolves #17891

Summary by CodeRabbit

  • New Features
    • Added Rinkel actions: Get Call Details, Get Call Recording, Get Voicemail, Add Note, Update Note.
    • Introduced an instant “Call Ended” trigger that emits events when calls finish.
    • Enhanced Rinkel integration with dynamic selectors to easily choose Call IDs, Recording IDs, Voicemail IDs, and Note IDs.
  • Chores
    • Bumped Rinkel component version to 0.1.0 and updated dependencies.

@vercel
Copy link

vercel bot commented Aug 25, 2025

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

2 Skipped Deployments
Project Deployment Preview Comments Updated (UTC)
pipedream-docs Ignored Ignored Aug 25, 2025 7:16pm
pipedream-docs-redirect-do-not-edit Ignored Ignored Aug 25, 2025 7:16pm

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Aug 25, 2025

Walkthrough

Adds Rinkel integration features: new webhook source for call-ended, base webhook, and multiple actions (get call details, get call recording, get voicemail, add note, update note). Updates app module with HTTP client, propDefinitions, and API methods. Bumps package version and adds @pipedream/platform dependency.

Changes

Cohort / File(s) Summary
Rinkel Actions
components/rinkel/actions/add-note/add-note.mjs, components/rinkel/actions/update-note/update-note.mjs, components/rinkel/actions/get-call-details/get-call-details.mjs, components/rinkel/actions/get-call-recording/get-call-recording.mjs, components/rinkel/actions/get-voicemail/get-voicemail.mjs
Introduce action modules to add/update notes, fetch call details, retrieve call recordings, and get voicemails. Each exports a default action with props, version, description, and run implementation calling corresponding app methods.
App Module Enhancements
components/rinkel/rinkel.app.mjs
Adds axios-based request wrapper, base URL, webhook helpers, resource list/get methods, and propDefinitions for dynamic options (callId, recordingId, voicemailId, noteId). Removes prior authKeys.
Webhook Source (Base + Event)
components/rinkel/sources/common/base-webhook.mjs, components/rinkel/sources/call-ended/call-ended.mjs, components/rinkel/sources/call-ended/test-event.mjs
Implements base webhook with activate/deactivate and run; adds “Call Ended (Instant)” source using base and sample event for testing.
Package Manifest
components/rinkel/package.json
Bumps version to 0.1.0 and adds dependency on @pipedream/platform.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  participant R as Rinkel API
  participant S as Base Webhook Source
  participant U as User Source (Call Ended)

  rect rgba(230,240,255,0.5)
  note right of S: Activation
  U->>S: activate()
  S->>R: POST /webhooks {event: getEvent(), url, active:true}
  R-->>S: 201 Created
  end

  rect rgba(240,230,255,0.5)
  note right of S: Incoming Event
  R-->>S: POST callEnd payload
  S->>S: generateMeta(event)
  S-->>U: $emit(body, meta)
  end

  rect rgba(255,240,230,0.5)
  note right of S: Deactivation
  U->>S: deactivate()
  S->>R: DELETE /webhooks {event: getEvent()}
  R-->>S: 204 No Content
  end
Loading
sequenceDiagram
  autonumber
  participant A as Action (Add/Update Note)
  participant App as Rinkel App Client
  participant API as Rinkel API

  A->>App: addNote / updateNote(params)
  App->>API: PATCH /calls/{id}/notes (or /calls/{callId}/notes/{noteId})
  API-->>App: 200 OK (note data)
  App-->>A: response
  A-->>A: $.export("$summary", ...)
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Assessment against linked issues

Objective Addressed Explanation
Source: call.end webhook [#17891]
Action: Get call details (GET /calls/{call_id}) [#17891]
Action: Get AI summary (GET /calls/{call_id}/insights) [#17891] Missing action and client method for insights endpoint.
Action: Get call recording (GET /calls/{call_id}/recording) [#17891] Implemented via recordingId prop and getCallRecording; unclear if endpoint accepts recordingId vs call_id.
Action: Get voicemail (GET /calls/{call_id}/voicemail) and Add/Update notes (PATCH /calls/{call_id}/notes) [#17891] Voicemail implemented via voicemailId; notes implemented (add/update), but update requires noteId; endpoint shape vs issue spec unclear.

Poem

I tap my paws on cables bright,
A call goes “end” — I log the night.
I fetch, I note, recordings too,
With voicemails queued in tidy view.
Patch-patch! I nibble bugs to crumbs,
Version hops — my carrot drums. 🥕🐇

Tip

🔌 Remote MCP (Model Context Protocol) integration is now available!

Pro plan users can now connect to remote MCP servers from the Integrations page. Connect with popular remote MCPs such as Notion and Linear to add more context to your reviews and chats.

✨ Finishing Touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch issue-17891

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
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

CodeRabbit Commands (Invoked using PR/Issue comments)

Type @coderabbitai help to get the list of available commands.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Status, Documentation and Community

  • Visit our Status Page to check the current availability of CodeRabbit.
  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

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: 0

🧹 Nitpick comments (12)
components/rinkel/sources/common/base-webhook.mjs (1)

10-24: Webhook subscribe/unsubscribe flow aligns with Rinkel API

POST /webhooks/:event with url/contentType/active, and DELETE /webhooks/:event are the documented endpoints and event names (incomingCall, outgoingCall, callStart, callEnd, callInsights). This matches your activate/deactivate logic. Consider optionally sending a description to help users identify the subscription in Rinkel. (developers.rinkel.com)

Proposed tweak (optional):

       await this.rinkel.createWebhook({
         event: this.getEvent(),
         data: {
           url: this.http.endpoint,
           contentType: "application/json",
           active: true,
+          description: "Pipedream webhook",
         },
       });
components/rinkel/rinkel.app.mjs (5)

7-21: callId options: pagination params OK; consider larger page size

Sorting by date DESC matches the API defaults. You can reduce UI pagination by requesting more items per page. (developers.rinkel.com)

         const response = await this.listCallDetailRecords({
           params: {
             page: page + 1,
+            perPage: 50,
             sort: "date",
             sortOrder: "DESC",
           },
         });

22-36: recordingId options: OK for “Recordings” (not “Call Recordings”)

This lists reusable “recordings” (greetings, MOH, etc.), which is consistent with GET /recordings. If the intent is “call recordings,” this is a different resource and selection flow. See comments on the action file. (developers.rinkel.com)

Also consider the same perPage optimization:

         const response = await this.listCallRecordings({
           params: {
             page: page + 1,
+            perPage: 50,
             sort: "date",
             sortOrder: "DESC",
           },
         });

37-51: voicemailId options: undocumented “sort” param

The List voicemail records endpoint documents sortOrder but not sort. Recommend dropping sort to avoid potential 400s on stricter validation. (developers.rinkel.com)

         const response = await this.listVoicemails({
           params: {
             page: page + 1,
-            sort: "date",
             sortOrder: "DESC",
           },
         });

52-66: noteId options: guard for calls without notes

Some CDRs may have no notes; add a safe default before mapping. (developers.rinkel.com)

-        const notes = response.data.notes;
-        return notes.map((note) => ({
+        const notes = response.data?.notes ?? [];
+        return notes.map((note) => ({
           label: note.content,
           value: note.id,
         }));

109-116: Method name suggests “call recording” but hits the “recordings” resource

GET /recordings/:id retrieves a reusable Recording (greetings/MOH), not a call recording. Either rename to getRecording (and keep a compatibility alias), or change the implementation and the prop to target call recordings. Right now the naming may confuse users and action authors. (developers.rinkel.com)

Non-breaking improvement:

     getCallRecording({
       id, ...opts
     }) {
       return this._makeRequest({
         path: `/recordings/${id}`,
         ...opts,
       });
     },
+    // Alias for clarity: "Recording" refers to reusable assets (not call recordings)
+    getRecording({ id, ...opts }) {
+      return this._makeRequest({
+        path: `/recordings/${id}`,
+        ...opts,
+      });
+    },
components/rinkel/actions/get-call-recording/get-call-recording.mjs (1)

4-16: Action name/docs don’t match the resource you’re calling

This action is titled “Get Call Recording” but calls GET /recordings/:id (a non-call “Recording”). Either:

  • Rename the action to “Get Recording” (and keep the same implementation), or
  • Switch to the Call Recording resource (requires a different prop and endpoint such as /call-recordings/:id/stream or using playUrl from a Call Detail Record). (developers.rinkel.com)

Minimal rename option:

-  key: "rinkel-get-call-recording",
-  name: "Get Call Recording",
-  description: "Get a call recording. [See the documentation](https://developers.rinkel.com/docs/api/get-a-recording)",
+  key: "rinkel-get-recording",
+  name: "Get Recording",
+  description: "Get a recording. [See the documentation](https://developers.rinkel.com/docs/api/get-a-recording)",

If you actually want call recordings, I can propose a revised action that selects a Call Detail Record, extracts callRecording.id, and fetches the audio URL.

components/rinkel/actions/get-voicemail/get-voicemail.mjs (2)

23-23: Nit: clarify the summary to reflect an expiring URL

Small UX win: communicate that the URL is temporary (3 hours), mirroring the docs. (developers.rinkel.com)

-    $.export("$summary", `Voicemail ${this.voicemailId} retrieved`);
+    $.export("$summary", `Temporary URL (3h) generated for voicemail ${this.voicemailId}`);

9-17: Optional: expose the language query param

Verified that getVoicemail in components/rinkel/rinkel.app.mjs destructures ...opts, so it forwards arbitrary options (including params) to the HTTP client. To let users request a localized stream/download URL, you can add a language prop in components/rinkel/actions/get-voicemail/get-voicemail.mjs:

   props: {
     rinkel,
     voicemailId: {
       propDefinition: [
         rinkel,
         "voicemailId",
       ],
     },
+    language: {
+      type: "string",
+      label: "Language",
+      description: "Optional language code for the returned URL (defaults to user language).",
+      optional: true,
+    },
   },
   async run({ $ }) {
     const response = await this.rinkel.getVoicemail({
       $,
       id: this.voicemailId,
+      params: this.language ? { language: this.language } : undefined,
     });
     $.export("$summary", `Voicemail ${this.voicemailId} retrieved`);
     return response;
   },

This enhancement is fully compatible with the existing method signature and is purely optional.

components/rinkel/actions/add-note/add-note.mjs (1)

17-21: Optional: trim content before sending

Avoids storing accidental leading/trailing whitespace.

   async run({ $ }) {
     const response = await this.rinkel.addNote({
       $,
       id: this.callId,
       data: {
-        content: this.content,
+        content: this.content?.trim(),
       },
     });

Also applies to: 24-30

components/rinkel/actions/update-note/update-note.mjs (1)

26-30: Optional: trim content before update

Same rationale as “Add Note”; avoids noisy diffs in downstream systems.

   content: {
     type: "string",
     label: "Content",
     description: "The content of the note",
   },
 ...
       data: {
-        content: this.content,
+        content: this.content?.trim(),
       },

Also applies to: 37-39

components/rinkel/sources/call-ended/call-ended.mjs (1)

17-23: Optional: guard against invalid datetime

Highly unlikely here, but if datetime is ever missing or malformed, Date.parse returns NaN. You can fall back to Date.now() to keep emission robust.

   generateMeta(event) {
-    return {
-      id: event.id,
-      summary: `Call ended: ${event.cause}`,
-      ts: Date.parse(event.datetime),
-    };
+    const tsParsed = Date.parse(event.datetime);
+    return {
+      id: event.id,
+      summary: `Call ended: ${event.cause}`,
+      ts: Number.isFinite(tsParsed) ? tsParsed : Date.now(),
+    };
   },
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 4fb872c and 8a039bd.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (10)
  • components/rinkel/actions/add-note/add-note.mjs (1 hunks)
  • components/rinkel/actions/get-call-details/get-call-details.mjs (1 hunks)
  • components/rinkel/actions/get-call-recording/get-call-recording.mjs (1 hunks)
  • components/rinkel/actions/get-voicemail/get-voicemail.mjs (1 hunks)
  • components/rinkel/actions/update-note/update-note.mjs (1 hunks)
  • components/rinkel/package.json (2 hunks)
  • components/rinkel/rinkel.app.mjs (1 hunks)
  • components/rinkel/sources/call-ended/call-ended.mjs (1 hunks)
  • components/rinkel/sources/call-ended/test-event.mjs (1 hunks)
  • components/rinkel/sources/common/base-webhook.mjs (1 hunks)
🧰 Additional context used
🧠 Learnings (2)
📚 Learning: 2024-07-24T02:06:47.016Z
Learnt from: GTFalcao
PR: PipedreamHQ/pipedream#12697
File: components/salesforce_rest_api/sources/common-webhook-methods.mjs:1-71
Timestamp: 2024-07-24T02:06:47.016Z
Learning: The `common-webhook-methods.mjs` object is designed to be extended, similar to an abstract class, and intentionally does not implement certain methods like `generateWebhookMeta` and `getEventType` to enforce implementation in subclasses.

Applied to files:

  • components/rinkel/sources/common/base-webhook.mjs
📚 Learning: 2024-12-12T19:23:09.039Z
Learnt from: jcortes
PR: PipedreamHQ/pipedream#14935
File: components/sailpoint/package.json:15-18
Timestamp: 2024-12-12T19:23:09.039Z
Learning: When developing Pipedream components, do not add built-in Node.js modules like `fs` to `package.json` dependencies, as they are native modules provided by the Node.js runtime.

Applied to files:

  • components/rinkel/package.json
🧬 Code graph analysis (7)
components/rinkel/actions/get-call-details/get-call-details.mjs (3)
components/rinkel/actions/get-call-recording/get-call-recording.mjs (1)
  • response (19-22)
components/rinkel/actions/get-voicemail/get-voicemail.mjs (1)
  • response (19-22)
components/rinkel/rinkel.app.mjs (4)
  • response (12-18)
  • response (27-33)
  • response (42-48)
  • response (57-59)
components/rinkel/sources/call-ended/call-ended.mjs (1)
components/rinkel/sources/common/base-webhook.mjs (1)
  • event (35-35)
components/rinkel/actions/add-note/add-note.mjs (1)
components/rinkel/actions/update-note/update-note.mjs (1)
  • response (33-40)
components/rinkel/actions/get-call-recording/get-call-recording.mjs (4)
components/rinkel/actions/add-note/add-note.mjs (1)
  • response (24-30)
components/rinkel/actions/get-call-details/get-call-details.mjs (1)
  • response (19-22)
components/rinkel/actions/get-voicemail/get-voicemail.mjs (1)
  • response (19-22)
components/rinkel/rinkel.app.mjs (4)
  • response (12-18)
  • response (27-33)
  • response (42-48)
  • response (57-59)
components/rinkel/actions/update-note/update-note.mjs (5)
components/rinkel/actions/add-note/add-note.mjs (1)
  • response (24-30)
components/rinkel/actions/get-call-details/get-call-details.mjs (1)
  • response (19-22)
components/rinkel/actions/get-call-recording/get-call-recording.mjs (1)
  • response (19-22)
components/rinkel/actions/get-voicemail/get-voicemail.mjs (1)
  • response (19-22)
components/rinkel/rinkel.app.mjs (4)
  • response (12-18)
  • response (27-33)
  • response (42-48)
  • response (57-59)
components/rinkel/actions/get-voicemail/get-voicemail.mjs (5)
components/rinkel/actions/add-note/add-note.mjs (1)
  • response (24-30)
components/rinkel/actions/get-call-details/get-call-details.mjs (1)
  • response (19-22)
components/rinkel/actions/get-call-recording/get-call-recording.mjs (1)
  • response (19-22)
components/rinkel/actions/update-note/update-note.mjs (1)
  • response (33-40)
components/rinkel/rinkel.app.mjs (4)
  • response (12-18)
  • response (27-33)
  • response (42-48)
  • response (57-59)
components/rinkel/rinkel.app.mjs (5)
components/rinkel/actions/add-note/add-note.mjs (1)
  • response (24-30)
components/rinkel/actions/get-call-details/get-call-details.mjs (1)
  • response (19-22)
components/rinkel/actions/get-call-recording/get-call-recording.mjs (1)
  • response (19-22)
components/rinkel/actions/get-voicemail/get-voicemail.mjs (1)
  • response (19-22)
components/rinkel/actions/update-note/update-note.mjs (1)
  • response (33-40)
⏰ 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). (4)
  • GitHub Check: Publish TypeScript components
  • GitHub Check: Verify TypeScript components
  • GitHub Check: pnpm publish
  • GitHub Check: Lint Code Base
🔇 Additional comments (15)
components/rinkel/package.json (2)

15-17: Dependency addition looks right for axios usage via the Platform SDK

Adding @pipedream/platform is the correct way to use axios in components. No built-in Node modules are added, so we’re good w.r.t. prior guidance.


3-3: Version bump matches a feature addition

Minor bump to 0.1.0 makes sense given the new app methods, actions, and sources.

components/rinkel/sources/common/base-webhook.mjs (2)

27-33: Abstract-method pattern is appropriate

Throwing ConfigurationError for unimplemented methods is the right contract for subclasses.


34-41: Run handler is minimal and correct

Early return on missing body and emitting with meta is fine for Pipedream HTTP sources. No changes needed.

components/rinkel/rinkel.app.mjs (3)

72-82: Auth header is correct

x-rinkel-api-key is the documented authentication header. Nice. (developers.rinkel.com)


143-160: Notes endpoints look correct

PUT /call-detail-records/:id/note and PATCH /call-detail-records/:id/note/:noteId match the docs. (developers.rinkel.com)


117-124: Voicemail endpoint likely incorrect (returns audio stream, not JSON)

You’re calling /voicemails/:id/stream, which streams audio. Typical actions want a temporary URL (JSON) from GET /voicemails/:id. Also, if you really want to stream, axios should set responseType: "stream"/"arraybuffer". As written, this may fail or return binary unexpectedly. Recommend switching to GET /voicemails/:id. (developers.rinkel.com)

-    getVoicemail({
-      id, ...opts
-    }) {
-      return this._makeRequest({
-        path: `/voicemails/${id}/stream`,
-        ...opts,
-      });
-    },
+    getVoicemail({ id, ...opts }) {
+      // Returns a temporary URL to stream/download the voicemail (JSON)
+      return this._makeRequest({
+        path: `/voicemails/${id}`,
+        method: "GET",
+        ...opts,
+      });
+    },

Likely an incorrect or invalid review comment.

components/rinkel/actions/get-call-recording/get-call-recording.mjs (1)

18-25: Run logic is fine given the current implementation

Given the endpoint returns JSON metadata for recordings, exporting a concise summary and returning the response is sufficient.

components/rinkel/actions/get-call-details/get-call-details.mjs (2)

3-16: Props are correctly wired to the app’s callId selector

Using the shared propDefinition keeps things consistent across actions.


18-25: Endpoint and summary align with docs

GET /call-detail-records/:id is the correct endpoint; the user-facing summary is clear. (developers.rinkel.com)

components/rinkel/actions/get-voicemail/get-voicemail.mjs (1)

18-24: Good: action correctly proxies the Voicemail “temporary URL” endpoint

The param shape and behavior match the Rinkel docs (path param id of the voicemail, returns a temporary URL that expires in ~3 hours). Looks consistent with the app wrapper pattern used elsewhere. (developers.rinkel.com)

components/rinkel/actions/add-note/add-note.mjs (1)

23-32: Good: conforms to “Add note to CDR” API

Using id for the Call Detail Record and content in the body matches the API. This action follows the same pattern as the other Rinkel actions in this PR. (developers.rinkel.com)

components/rinkel/actions/update-note/update-note.mjs (1)

32-41: Good: uses correct identifiers for update — callId + noteId

Matches the API route semantics for updating a note on a CDR. The cross-field dependency in the noteId propDefinition is a nice touch for DX. (developers.rinkel.com)

components/rinkel/sources/call-ended/test-event.mjs (1)

1-5: LGTM: sample payload matches docs for the “call end” webhook

Fields id, datetime, and cause align with the documented payload for call end events. (developers.rinkel.com)

components/rinkel/sources/call-ended/call-ended.mjs (1)

12-16: Correct event name: callEnd

The event identifier matches the official list of webhook events used when subscribing via API. (developers.rinkel.com)

Copy link
Collaborator

@GTFalcao GTFalcao left a comment

Choose a reason for hiding this comment

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

LGTM!

@vunguyenhung vunguyenhung merged commit 5403da4 into master Aug 26, 2025
10 checks passed
@vunguyenhung vunguyenhung deleted the issue-17891 branch August 26, 2025 01:31
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.

Rinkel

4 participants