How to Sync Won Deals from Your CRM's REST API into NetSuite and Stripe
Build a scheduled Spojit workflow that calls your PSA or CRM's REST API to pull newly won deals, then loops over each one to create a matching customer in NetSuite and Stripe.
What This Integration Does
Professional-services teams close deals in a CRM or PSA (a project-and-resourcing tool), but the moment a deal is won, finance still needs that account to exist in the ERP and in the payments system before they can invoice or bill it. Doing that by hand means re-keying the same company name, email, and address into two places, which is slow and error-prone. This workflow removes the double entry: when a deal flips to "won" in your CRM, Spojit picks it up on a schedule and provisions the account in both NetSuite and Stripe automatically.
The workflow runs on a Schedule trigger, for example every weekday morning. On each run it calls your CRM's REST API for deals won since the last run, loops over the results, and for each deal upserts a NetSuite customer keyed by an external ID and creates a Stripe customer. Because the NetSuite write is an upsert keyed by the deal's own ID, re-running the workflow (or overlapping windows) updates the existing record instead of creating duplicates, so the run is safe to repeat. The Stripe step is guarded by a Condition node so you only create a payment customer when the deal carries a billing email.
Prerequisites
- An API key (or bearer token) for your CRM or PSA's REST API, and the endpoint that lists won deals with a date filter.
- A NetSuite connection added under Connections, with permission to create and update customer records. See the NetSuite connector article.
- A Stripe connection added under Connections with a key that can create customers. See the Stripe connector article.
- A decision on which CRM field becomes the NetSuite
externalId(the deal ID or company ID works well as a stable key).
Step 1: Start the workflow on a Schedule trigger
Add a Trigger node and set its type to Schedule. Enter a 5-field cron expression and an IANA timezone so the run lands in your working hours, for example 0 8 * * 1-5 with Australia/Sydney to run at 08:00 every weekday. The trigger output is { scheduledAt }, which you can reference later as {{ trigger.scheduledAt }} to build the "won since" date window. A single trigger can hold more than one schedule if you want a second sweep in the afternoon.
Step 2: Fetch won deals from your CRM's REST API with the http connector
Your CRM or PSA has no native tile in Spojit, so reach it honestly through the http connector. Add a Connector node, choose the http connector in Direct mode, and pick the http-get tool. Set url to your CRM's list-deals endpoint with a filter for won deals updated since the last run, and pass your credentials in headers:
url: https://api.yourcrm.com/v1/deals?stage=won&updated_after={{ trigger.scheduledAt }}
headers:
Authorization: Bearer YOUR_API_KEY
Accept: application/json
The http-get response includes the parsed JSON body, the HTTP status, and the response headers. Store the node's output variable (for example crm) so the next steps can read the deal array as {{ crm.data.deals }}. If your API names the array differently, adjust the path to match the JSON it returns. For a deeper look at this pattern, see how to connect to any REST API using HTTP requests.
Step 3: Loop over each won deal
Add a Loop node in ForEach mode and point it at the deal array from the previous step, for example {{ crm.data.deals }}. Each iteration exposes the current deal as the loop item (for example {{ deal }}), so the connector steps inside the loop body run once per won deal. Keep the loop body lean: a Condition, a NetSuite upsert, and a Stripe create. If your CRM paginates results, fetch additional pages in Step 2 first (a second http-get against the next link) and merge them before the loop, since a single page may not contain every won deal.
Step 4: Upsert the account as a NetSuite customer
Inside the loop, add a Connector node on the netsuite connector in Direct mode and choose the upsert-record tool. This creates or updates a record keyed by its external ID, so the same deal always maps to the same NetSuite customer. Set recordType to customer, set externalId to the deal's stable key from the CRM, and put the customer fields in body:
recordType: customer
externalId: {{ deal.id }}
body:
companyName: {{ deal.company }}
email: {{ deal.contactEmail }}
comments: Won deal synced from CRM on {{ trigger.scheduledAt }}
Map the body fields to the names your NetSuite account expects. Because this is an upsert keyed by externalId, a repeated run updates the existing customer rather than creating a duplicate, which is what makes the whole workflow safe to re-run.
Step 5: Only continue to Stripe when there is a billing email
Stripe customers are far more useful with an email attached, so add a Condition node after the NetSuite step. Configure the true branch to check that the deal carries a billing email, for example "{{ deal.contactEmail }} is not empty". Route the true branch into the Stripe step in the next step, and leave the false branch empty so deals with no email still get their NetSuite customer without failing the run. This keeps incomplete CRM records from blocking the rest of the loop.
Step 6: Create the Stripe customer on the true branch
On the Condition node's true branch, add a Connector node on the stripe connector in Direct mode and choose the create-customer tool. Map the deal fields to the customer inputs:
name: {{ deal.company }}
email: {{ deal.contactEmail }}
metadata:
crm_deal_id: {{ deal.id }}
netsuite_external_id: {{ deal.id }}
Writing the CRM deal ID into metadata gives you a back-reference so finance can trace a Stripe customer to its source deal and matching NetSuite record. You can also pass phone and an address object if your CRM provides them. If you later want to notify the account owner that provisioning is done, add a Send Email node after the loop using Spojit's built-in mail service.
Step 7: Save and confirm the layout
Save the workflow and confirm the shape: Schedule trigger, an http-get Connector node, a Loop wrapping the upsert-record Connector node, then a Condition whose true branch feeds the create-customer Connector node. If you want Spojit to scaffold or rearrange this for you, ask Miraxa, the intelligent layer across your automation, something specific like "Add a Loop over {{ crm.data.deals }} with a NetSuite upsert-record node and a Stripe create-customer node on a Condition true branch." If an instruction is ambiguous, Miraxa asks before changing the canvas.
Tips
- Use a Transform node right after Step 2 to reshape the raw CRM payload into a clean array of
{ id, company, contactEmail }objects. A tidy shape makes every downstream mapping easier to read and less brittle when the API changes. - Drive the "won since" window from
{{ trigger.scheduledAt }}so the run only asks for deals changed since the schedule fired, which keeps each page small and the run fast. - Keep the loop body to the three connector calls. If you need a notification per deal, prefer a single Send Email summary after the loop over one email per iteration.
- Store the NetSuite
externalIdin Stripemetadata(and vice versa) so the two systems stay cross-referenced for reconciliation.
Common Pitfalls
- Timezone drift: the Schedule cron runs in the IANA timezone you set, but your CRM's
updated_afterfilter may expect UTC. Convert the window explicitly with a Transform step so you do not silently miss or double-count deals around midnight. - Pagination:
http-getreturns one page. If your CRM caps results per page, fetch and merge the remaining pages before the loop, or won deals beyond the first page will never sync. - Unstable external IDs: if you key
upsert-recordon a field that the CRM can change, you will create duplicate NetSuite customers. Pick an immutable identifier such as the deal or company ID. - Empty emails reaching Stripe: without the Condition guard, deals missing a contact email still create a Stripe customer with no email, which is hard to reconcile later. Keep the guard in place.
Testing
Before scheduling it, point Step 2 at a query that returns just one or two known won deals (for example a single test deal in your CRM), then run the workflow from the designer and watch it in the execution log. Confirm the http-get response contains the deals you expect, that the NetSuite upsert-record step created or updated the right customer, and that the Stripe create-customer step only fired for deals with an email. Run it a second time with the same data to prove the upsert updates rather than duplicates. Once the small scope behaves, widen the date window and enable the schedule.