Working demo, sample data, no login

Open a pre-filled form straight from the deal, and write the answers back to that same deal

This is the exact workflow from a HubSpot custom CRM card: the rep opens the form from inside the deal, the form is already filled with that deal's details, it has conditional logic, and on save it updates the same deal instead of creating a new record. There is also a client-fillable link version.

Nothing to install. Click through it below the way a rep would during a call.

View as
1 Open the deal
2 Open form from the card
3 It is pre-filled
4 Conditional questions
5 Save back to the same deal
S Contacts Companies Deals Workflows Sales Hub
DealsQ3 PipelineNorthwind Trading, fleet upfitting
NT

Northwind Trading, fleet upfitting

Deal record  ·  ID 8829301  ·  owner Sam Rivera
$18,400
Amount
Appointment
Qualified
Presentation
Decision
Closed won
About this deal
Company
Northwind Trading Co.
Primary contact
Jordan Avery
Deal stage
Presentation scheduled
Amount
$18,400
Close date
Aug 29, 2026
Scoping (updated by the form)
Scoping status
Not started
Primary need
Empty
Vehicles in scope
Empty
Target timeline
Empty
Last scoping update
Empty
Overview Activities Notes Calls
Call scheduled with Jordan Avery
Discovery and scoping call, 30 min.
Today, 10:00 AM
Email opened, proposal preview
Jordan opened the deck twice.
Yesterday, 4:12 PM
Deal actions
Scoping call form Custom card
Not completed yet
Opens pre-filled with this deal. Answers save back here.
Also on this deal
📄
PandaDoc
Proposal, sent
👤
Jordan Avery
Contact

The card in the right column is the custom part. Everything else is a normal HubSpot deal.

Everything here is made-up sample data. Northwind Trading, Jordan Avery, the deal, and the numbers are all invented for the demo. There is no real account behind it and nothing is stored.

What makes the pre-fill and write-back work

Off-the-shelf form tools connect to a deal after submission and match records by email, which is why they can create duplicates and cannot pre-fill from the deal. A CRM card runs the other direction.

1
The card knows the deal
HubSpot hands the card the deal ID when the rep opens it. No matching, no guessing.
2
It loads live values
The card reads the current deal properties and fills the form before it opens.
3
Conditional questions
Fields show or hide based on answers, so the rep only sees what is relevant.
4
Save updates that deal
On submit it patches the same deal ID. The client link carries that ID too, so both write to one record.
The actual extension code that does this show ▾

This is a real HubSpot developer-projects CRM card, not a mockup. A HubSpot admin runs hs project upload and it appears in the deal sidebar. Two pieces do the work.

src/app/extensions/ScopingCard.jsx  (the card, opens the pre-filled form)
// Renders in the deal sidebar. HubSpot passes the deal id in context,
// so the form is filled from THIS deal before it opens.
import React, { useState, useEffect } from 'react';
import { Button, Form, Select, Input, Text, hubspot } from '@hubspot/ui-extensions';

hubspot.extend(({ context }) => <ScopingCard dealId={context.crm.objectId} />);

function ScopingCard({ dealId }) {
  const [deal, setDeal] = useState(null);
  const [need, setNeed] = useState('');

  useEffect(() => {                              // load this deal's live values
    hubspot.serverless('getDeal', { propertiesToSend: ['hs_object_id', 'dealname', 'amount'] })
      .then(setDeal);
  }, []);

  if (!deal) return <Text>Loading this deal...</Text>;

  const save = (v) =>
    hubspot.serverless('saveScoping', { propertiesToSend: ['hs_object_id'], parameters: { ...v, need } });

  return (
    <Form onSubmit={save}>
      <Text>{deal.dealname} · ${deal.amount}</Text>
      <Select name="need" label="Primary need" required onChange={setNeed}
        options={[{label:'Fleet upfitting',value:'fleet'},
                 {label:'Single vehicle',value:'single'}]} />
      {need === 'fleet' && <Input name="vehicles" label="How many vehicles?" />}
      {need === 'single' && <Input name="model" label="Make and model" />}
      <Button type="submit" variant="primary">Save to deal</Button>
    </Form>
  );
}
src/app/app.functions/saveScoping.js  (writes back to the same deal)
// PATCHes the SAME deal id. No new record, no email matching.
const hubspot = require('@hubspot/api-client');

exports.main = async (context = {}) => {
  const dealId = context.propertiesToSend.hs_object_id;
  const { need, vehicles, model, timeline, notes } = context.parameters;
  const client = new hubspot.Client({ accessToken: process.env['PRIVATE_APP_ACCESS_TOKEN'] });

  await client.crm.deals.basicApi.update(dealId, {
    properties: {
      scoping_status:   'completed',
      scoping_need:     need,
      scoping_vehicles: vehicles || '',
      scoping_model:    model || '',
      scoping_timeline: timeline || '',
      scoping_notes:    notes || '',
      scoping_updated:  new Date().toISOString()
    }
  });
  return { ok: true, dealId };
};

The client link version is the same form served as a standalone page. It reads the deal id from the URL and calls the same save function, so the rep card and the client link both write to one record.

⇩ Download the full extension source

Built by an independent developer as a working example for the r/hubspot thread about opening a pre-filled form from the deal. Sample data only.

If it is useful, it can be adapted to your real fields and pipeline and installed on your own HubSpot.