Series: Building Quorentra CRM from Zero — A Modular, ChatGPT-Native AI CRM
Building conversational Opportunity creation with server-side drafts, missing-field detection, business defaults, Company resolution, validation, confirmation, idempotency, transactions, and audit.

1. Introduction
In Part 18, Quorentra crossed an important boundary.
For the first time, ChatGPT could participate in changing authoritative CRM state.
A user could say:
Move the Microsoft 365 Migration opportunity to negotiation.
Quorentra did not immediately execute the request.
Instead, we built a governed mutation pipeline:
User Intent ↓Entity Resolution ↓Prepare Mutation ↓Validate Current State ↓Confirmation ↓Execute ↓Audit ↓Authoritative Result
That architecture gave us a safe first mutation.
But updating an existing Opportunity was relatively simple.
We already knew:
OpportunityCurrent StageRequested Stage
Creating a new Opportunity is fundamentally differen
Consider:
Create an opportunity for Contoso.
What should Quorentra create?
We know the Company.
But we may not know:
Opportunity NameAmountCurrencyExpected Close DateProbabilityDescription
ChatGPT must not invent those facts merely to complete the operation.
Part 19 therefore introduces a new concept:
Conversational CRM drafts.
Instead of converting incomplete natural language directly into database writes, Quorentra will progressively construct a structured Opportunity draft.
Only when the draft is complete and valid can it become a prepared creation mutation.
And only after confirmation can that mutation create an authoritative CRM record.
2. What Are We Building?
Our target interaction begins simply:
Create a new opportunity for Contoso.
ChatGPT resolves:
Company:Contoso
but determines that required information is missing.
It may ask:
What should the opportunity be called?
The user replies:
Microsoft Copilot Deployment.
Then:
What is the estimated value?
User:
€120,000.
Then:
What is the expected close date?
User:
November 30.
Quorentra progressively builds:
Opportunity DraftCompany:ContosoName:Microsoft Copilot DeploymentAmount:€120,000Currency:EURStage:QualificationProbability:20%Expected Close:30 November 2026
When all required information is available, Quorentra presents:
┌────────────────────────────────────────────┐│ QUORENTRA ││ Create Opportunity ││ ││ Company ││ Contoso ││ ││ Opportunity ││ Microsoft Copilot Deployment ││ ││ Value ││ €120,000 ││ ││ Stage ││ Qualification ││ ││ Probability ││ 20% ││ ││ Expected Close ││ 30 November 2026 ││ ││ This will create a new CRM record. ││ ││ [Cancel] [Create Opportunity] │└────────────────────────────────────────────┘
Only after:
Create Opportunity
does Quorentra insert the record into PostgreSQL.
3. Why Creation Is Harder Than Updating
Part 18 changed:
One existing entity+One known field+One requested value
Part 19 must solve:
Entity relationship+Multiple fields+Potentially missing values+Defaults+User-provided facts+Ambiguity+Validation+Duplicate awareness+Confirmation+Creation
That is a much richer workflow.
4. The Central Rule
The most important principle in Part 19 is:
ChatGPT must not silently invent missing CRM facts.
If the user says:
Create an opportunity for Contoso.
ChatGPT should not invent:
Name = Cloud Transformation ProjectAmount = €100,000Probability = 70%Close Date = next quarter
just because those values sound plausible.
Those are business facts.
They must come from:
The userAuthoritative CRM dataExplicit business rulesDeterministic defaults
not model imagination.
5. Defaults Are Different From Guesses
This distinction is critical.
Suppose Quorentra defines:
New Opportunity Stage:QualificationDefault Qualification Probability:20%
Then:
stage = qualificationprobability = 20
are legitimate business defaults.
They are deterministic.
By contrast:
amount = €75,000
because ChatGPT believes that sounds reasonable is a guess.
Quorentra must distinguish:
Business Default
from:
AI Guess
6. The New Creation Architecture
Our new workflow becomes:
User │ │ "Create an opportunity for Contoso." ▼ChatGPT │ ▼Resolve Company │ ▼Create Opportunity Draft │ ▼Quorentra │ ├── Apply Business Defaults ├── Validate Known Fields └── Identify Missing Fields │ ▼Draft Status │ ├── incomplete → ask user │ └── complete │ ▼Prepare Creation │ ▼Confirmation UI │ ▼User Confirms │ ▼Execute Creation │ ▼Transaction │ ├── Create Opportunity ├── Create AuditEvent └── Mark Mutation Executed │ ▼Authoritative Opportunity │ ▼ChatGPT + Widget
This extends the Part 18 mutation model rather than replacing it.
7. Reuse the Mutation Architecture
Part 18 gave us:
MutationRequestAuditEventConfirmationExpiryIdempotencyRBACTenant IsolationTransactions
We should reuse all of that.
Do not create a separate safety architecture just for record creation.
Our general mutation pattern remains:
Prepare ↓Confirm ↓Execute ↓Audit
Part 19 adds a new phase before preparation:
Draft
So the full creation pattern becomes:
Draft ↓Prepare ↓Confirm ↓Execute ↓Audit
8. Starting Checkpoint
Before implementing Part 19, verify Part 18.
Run:
cd backendpython -m pytest
Then:
cd ..\chatgpt-uinpm run build
Verify:
Show my EUR pipeline.
Open an Opportunity.
Then request:
Move Microsoft 365 Migration to negotiation.
Verify:
PrepareConfirmationExecutionAuditUpdated Opportunity
all work.
Part 19 depends directly on that mutation infrastructure.
9. Define the Create Permission
Part 18 introduced:
opportunities.update
Part 19 requires:
opportunities.create
Again:
readupdatecreate
must remain separate capabilities.
10. Example Role Matrix
Conceptually:
| Role | Read | Create | Update |
|---|---|---|---|
| Viewer | ✓ | — | — |
| Member | ✓ | ✓ | ✓ |
| Manager | ✓ | ✓ | ✓ |
| Admin | ✓ | ✓ | ✓ |
| Owner | ✓ | ✓ | ✓ |
The exact role model can evolve later.
The important point is capability separation.
11. What Fields Does an Opportunity Need?
Our current Opportunity domain contains approximately:
idorganization_idcompany_idnamedescriptionstageamountcurrencyprobabilityexpected_close_dateversioncreated_atupdated_at
Some are system-managed.
Others must be provided or defaulted.
12. Classify Every Field
Before building conversational creation, classify each field.
For example:
| Field | Source |
|---|---|
id | System generated |
organization_id | TenantContext |
company_id | Resolved CRM entity |
name | User required |
description | Optional user input |
stage | Business default |
amount | User required |
currency | User or contextual default |
probability | Stage business rule |
expected_close_date | User required |
version | System default |
created_at | System generated |
updated_at | System generated |
This classification is extremely useful.
13. Never Ask the User for Tenant ID
The user should never need to provide:
organization_id
The authenticated:
TenantContext
supplies it.
Likewise, ChatGPT should not send it.
This rule remains unchanged.
14. Never Ask the User for Record ID
The Opportunity:
id
is generated by Quorentra.
It is not conversational input.
The same applies to:
versioncreated_atupdated_at
These are system-managed fields.
15. Stage as a Business Default
For the MVP, define:
New Opportunity Stage:qualification
unless the user explicitly provides another allowed stage.
This means:
Create an opportunity for Contoso.
does not require ChatGPT to ask:
Which stage?
unless the user wants something different.
16. Probability as a Stage Default
We can define:
qualification = 20%discovery = 40%proposal = 60%negotiation = 80%
Then new:
Qualification
Opportunities default to:
20%
This is deterministic business logic.
17. Should Users Override Probability?
Eventually, yes.
Sales organizations often customize probability independently of stage.
For the MVP, we can allow an explicitly supplied probability.
Otherwise use the stage default.
The rule becomes:
Explicit valid user value ↓Use itNo explicit value ↓Use stage default
18. Currency Default
Currency is more interesting.
If the user’s active organization has:
default_currency = EUR
then Quorentra can safely default:
currency = EUR
That is an organization setting.
It is not an AI guess.
19. Avoid Locale Guessing
Do not infer:
EUR
solely because the user appears to be in Europe.
A user in Europe may be creating:
USDGBPCHFJPY
Opportunities.
Currency should come from:
Explicit user inputorOrganization business configuration
20. Expected Close Date
Should Quorentra default the expected close date?
Probably not.
This is a meaningful sales forecast.
The model should not invent:
30 days from today
unless Quorentra explicitly defines that as organizational policy.
For the MVP:
expected_close_date
is required user information.
21. Opportunity Name
Likewise:
name
should be required.
If the user says:
Create an opportunity for Contoso for their Microsoft Copilot deployment.
ChatGPT may reasonably extract:
Microsoft Copilot Deployment
from the user’s explicit wording.
That is extraction.
It is not invention.
22. Extraction Versus Generation
This distinction matters.
User says:
We have a €120,000 Microsoft Copilot deployment opportunity with Contoso expected to close November 30.
ChatGPT can extract:
Company:ContosoName:Microsoft Copilot DeploymentAmount:120000Currency:EURExpected Close:30 November
All of those facts came from the user.
ChatGPT merely structured them.
23. Natural Language Is Input
This is one of the strengths of a ChatGPT-native CRM.
Traditional CRM:
Open formFill companyFill nameFill amountSelect currencySelect stageEnter probabilityChoose dateSave
Quorentra:
Create a €120,000 Microsoft Copilot deployment opportunity for Contoso closing November 30.
The same structured data can be captured conversationally.
But the backend still validates everything.
24. Introduce OpportunityDraft
We now need a server-side representation of an incomplete proposed Opportunity.
Conceptually:
OpportunityDraft├── id├── organization_id├── user_id├── company_id├── name├── description├── stage├── amount├── currency├── probability├── expected_close_date├── status├── expires_at├── created_at└── updated_at
This is not yet an Opportunity.
25. Why Persist the Draft?
We could keep draft state entirely in ChatGPT conversation context.
But that would make the model responsible for preserving authoritative creation state.
A better design is:
Conversation ↓Draft updates ↓Quorentra ↓Authoritative draft
ChatGPT can help build the draft.
Quorentra owns it.
26. Conversation Context Is Not a Database
This principle is worth making explicit:
Chat history is context, not authoritative application state.
A conversation may be:
trimmedsummarizedrestartedinterruptedcontinued elsewhere
The CRM draft should survive independently where appropriate.
27. Draft Status
Possible statuses:
incompletecompletepreparedcancelledexpiredconverted
Initially:
incomplete
Once all required fields exist:
complete
After a MutationRequest is prepared:
prepared
After successful Opportunity creation:
converted
28. Draft Expiry
Drafts should not live forever.
For example:
expires_at =last_updated_at + 24 hours
or another configurable duration.
A longer lifetime than confirmation requests makes sense because conversational data collection may take time.
29. Draft Is Tenant-Scoped
Every draft belongs to:
organization_iduser_id
All reads and updates must enforce both.
A draft is not a globally accessible temporary record.
30. Create the Draft Model
Conceptually:
class OpportunityDraft(Base): __tablename__ = "opportunity_drafts" id = ... organization_id = ... user_id = ... company_id = ... name = ... description = ... stage = ... amount = ... currency = ... probability = ... expected_close_date = ... status = ... expires_at = ... created_at = ... updated_at = ...
Most business fields are nullable because the draft may be incomplete.
31. Draft Fields May Be Null
This is intentionally different from the authoritative Opportunity model.
For example:
Opportunity.amount
may be required.
But:
OpportunityDraft.amount
may be:
NULL
until the user supplies it.
That is the purpose of the draft.
32. Authoritative Records Remain Strict
Do not weaken the real Opportunity schema just because conversational drafts can be incomplete.
Keep:
Opportunity
strict.
Allow incompleteness only in:
OpportunityDraft
This maintains domain integrity.
33. Draft Creation Tool
Introduce:
create_opportunity_draft
This tool does not create an Opportunity.
It creates a working draft.
Conceptually:
Permission:opportunities.createMutation:Draft state onlyCRM Business Record Created:No
34. Draft Tool Input
The input should allow known fields.
Conceptually:
class CreateOpportunityDraftInput(BaseModel): company_reference: str | None = None name: str | None = None description: str | None = None stage: OpportunityStage | None = None amount: Decimal | None = None currency: str | None = None probability: int | None = None expected_close_date: date | None = None
However, Company resolution deserves special attention.
35. Do Not Trust a Company Name as an ID
The user says:
Contoso.
The database needs:
company_id
ChatGPT should not manufacture a UUID.
Instead:
"Contoso" ↓Company Resolution ↓company_id
The server owns this resolution process.
36. Company Resolution
We already have Company capabilities from earlier parts.
We can introduce or reuse:
search_companies
or equivalent Company lookup logic.
The resolution must be:
Tenant-scopedCase-awarePredictableAmbiguity-aware
37. Exact Company Match
Suppose Tenant A contains:
ContosoFabrikamNorthwind
The user says:
Contoso.
Quorentra finds one exact tenant-scoped match.
Then:
company_id = Contoso.id
can safely enter the draft.
38. Ambiguous Company Match
Suppose the tenant contains:
Contoso LtdContoso EuropeContoso Consulting
The user says:
Create an opportunity for Contoso.
Do not arbitrarily choose one.
Return:
Multiple companies match "Contoso".
Then ChatGPT can ask:
Which Contoso company do you mean?
39. Ambiguity Is Better Than Guessing
This will become a recurring Quorentra principle:
When authoritative entity resolution is ambiguous, ask rather than guess.
This applies later to:
ContactsOpportunitiesTasksUsersDocumentsMeetings
40. Company Not Found
If no Company exists:
Company "Contoso" was not found.
Do not silently create the Company as a side effect of creating an Opportunity.
That would combine two business operations.
Later we can support:
Create CompanythenCreate Opportunity
as an explicit multi-step workflow.
41. Why Not Auto-Create Missing Companies?
Because:
Create an opportunity for Acme.
could contain:
TypoNew companyExisting company under different nameTrading nameSubsidiary
Automatic creation can produce duplicate CRM records.
Explicit Company creation deserves its own governed operation.
42. Apply Business Defaults Server-Side
After Company resolution, the server applies:
stage = qualification
if no stage was provided.
Then:
probability = 20
if no probability was provided.
Then:
currency = organization.default_currency
if no currency was provided.
These rules belong in Quorentra.
43. Why Not Apply Defaults in ChatGPT?
Because business defaults should work identically from:
ChatGPTWidgetRESTFuture mobile clientAutomationAI Agent
Therefore:
Application Service
owns defaults.
Not the interface.
44. Draft Result
After creation, the tool might return:
{ "draft_id": "...", "status": "incomplete", "values": { "company": { "id": "...", "name": "Contoso" }, "name": null, "amount": null, "currency": "EUR", "stage": "qualification", "probability": 20, "expected_close_date": null }, "missing_fields": [ "name", "amount", "expected_close_date" ]}
This is extremely useful to ChatGPT.
45. Missing Fields Are Server-Determined
Do not make ChatGPT independently decide which fields are required.
Quorentra returns:
missing_fields
based on current business rules.
This allows requirements to evolve without rewriting prompts.
46. Example Conversation
User:
Create an opportunity for Contoso.
Quorentra creates:
Company: ContosoCurrency: EURStage: QualificationProbability: 20%Missing:NameAmountExpected Close Date
ChatGPT can then ask:
What should the opportunity be called?
47. Why Ask One Thing at a Time?
Conversationally, one concise question often works better than presenting a large form.
But ChatGPT may combine questions when appropriate.
For example:
What should the opportunity be called, what is its estimated value, and when do you expect it to close?
The important part is that missing fields come from Quorentra’s structured result.
48. Updating the Draft
Introduce:
update_opportunity_draft
Conceptually:
class UpdateOpportunityDraftInput(BaseModel): draft_id: UUID name: str | None = None description: str | None = None stage: OpportunityStage | None = None amount: Decimal | None = None currency: str | None = None probability: int | None = None expected_close_date: date | None = None
The server applies only provided fields.
49. Draft Update Is Not Final CRM Mutation
Updating:
OpportunityDraft
does not create or change an authoritative Opportunity.
Therefore it has a lower business impact.
Still, it must be:
AuthenticatedTenant-scopedUser-scopedValidated
because drafts may contain business information.
50. User Supplies the Name
ChatGPT asks:
What should the opportunity be called?
User:
Microsoft Copilot Deployment.
The tool calls:
update_opportunity_draft
with:
{ "draft_id": "...", "name": "Microsoft Copilot Deployment"}
Quorentra returns the updated draft.
51. Missing Fields Shrink
Now:
{ "missing_fields": [ "amount", "expected_close_date" ]}
ChatGPT knows what remains.
52. User Supplies Amount
User:
€120,000.
ChatGPT can extract:
amount = 120000currency = EUR
Then update the draft.
Because the user explicitly provided the currency symbol:
€
this is legitimate extraction.
53. Amount Validation
The backend validates:
amount >= 0
and whatever upper bounds or precision rules Quorentra defines.
Do not trust the model to validate monetary values.
54. Decimal, Not Float
For monetary values, continue using:
Decimal
rather than binary floating-point arithmetic.
For example:
Decimal("120000.00")
This keeps monetary calculations deterministic.
55. Currency Validation
Normalize currency to ISO-style codes:
EURUSDGBPCHFJPY
Do not store:
€EuroEUROeuros
as the authoritative currency field.
Presentation can use symbols.
Persistence should use a stable code.
56. Date Resolution
User:
November 30.
What year?
If the context makes the year unambiguous according to defined rules, ChatGPT may resolve it.
But date interpretation can be risky.
A safer interaction is:
November 30, 2026?
The user can confirm if necessary.
For authoritative forecasting dates, explicit dates are preferable.
57. Relative Dates
The user may say:
End of next month.
ChatGPT can interpret that into a date.
But Quorentra should receive the normalized:
YYYY-MM-DD
value.
The structured tool input should not contain vague text such as:
"end of next month"
unless a dedicated date-resolution layer exists.
58. Complete Draft
Eventually:
missing_fields = []
and:
status = complete
The draft may look like:
Company:ContosoName:Microsoft Copilot DeploymentAmount:€120,000Stage:QualificationProbability:20%Expected Close:30 November 2026
Now it is eligible for preparation.
59. Complete Does Not Mean Created
This distinction is essential.
Draft status = complete
means:
All required information is availableand structurally valid.
It does not mean:
Opportunity exists.
No Opportunity row has been inserted yet.
60. Duplicate Awareness
Before preparing creation, Quorentra should check for obvious potential duplicates.
For example:
Company:ContosoOpportunity:Microsoft Copilot Deployment
may already exist.
We do not necessarily block creation.
But we should detect relevant matches.
61. Duplicate Check
A simple MVP duplicate query can search the active tenant for:
same company+similar or exact opportunity name+open opportunity
An exact normalized name match is a good starting point.
62. Example Duplicate Warning
Quorentra may return:
Potential existing opportunity:Microsoft Copilot DeploymentContosoProposal€110,000
Then the user can decide whether to:
Open existing opportunityorContinue creating new opportunity
63. Do Not Overbuild Fuzzy Matching Yet
We do not need:
EmbeddingsVector similarityAI duplicate scoring
for the MVP.
Start with deterministic:
normalized companynormalized opportunity name
Later AI can improve duplicate detection.
64. Prepare Creation Tool
Once the draft is complete, introduce:
prepare_opportunity_creation
Input:
{ "draft_id": "..."}
The server then:
Loads draftChecks tenantChecks userChecks permissionChecks expiryRevalidates CompanyRevalidates all fieldsChecks missing fieldsChecks duplicatesCreates MutationRequestMarks draft prepared
No Opportunity is created yet.
65. Mutation Type
The Part 18 MutationRequest can now support:
mutation_type =opportunity.create
Its proposed values contain the complete Opportunity data.
For example:
{ "company_id": "...", "name": "Microsoft Copilot Deployment", "amount": "120000.00", "currency": "EUR", "stage": "qualification", "probability": 20, "expected_close_date": "2026-11-30"}
66. No Old Values for Creation
Part 18 recorded:
old valuenew value
For creation there is no prior entity state.
Therefore:
old_values = null
or:
{}
while:
new_values
contains the created record’s relevant business state.
67. Confirmation Result
prepare_opportunity_creation returns something like:
{ "mutation_request_id": "...", "draft_id": "...", "requires_confirmation": true, "opportunity": { "company_name": "Contoso", "name": "Microsoft Copilot Deployment", "amount": "120000.00", "currency": "EUR", "stage": "qualification", "probability": 20, "expected_close_date": "2026-11-30" }, "warnings": [], "expires_at": "..."}
This drives the confirmation UI.
68. Build the Creation Confirmation UI
Create:
chatgpt-ui/src/mutations/└── OpportunityCreationConfirmation.tsx
It should clearly show every material business field.
For example:
┌────────────────────────────────────────────┐│ QUORENTRA ││ Create Opportunity ││ ││ Company ││ Contoso ││ ││ Opportunity ││ Microsoft Copilot Deployment ││ ││ Value ││ €120,000 ││ ││ Stage ││ Qualification ││ ││ Probability ││ 20% ││ ││ Expected Close ││ 30 November 2026 ││ ││ This will create a new CRM record. ││ ││ [Cancel] [Create Opportunity] │└────────────────────────────────────────────┘
69. Confirmation Should Show Defaults
Suppose:
Stage = QualificationProbability = 20%Currency = EUR
came from business defaults.
They should still appear in the confirmation.
The user should see the actual record that will be created.
70. Do Not Hide AI Interpretation
Likewise, if ChatGPT extracted:
Microsoft Copilot Deployment
from a longer sentence, show it.
Confirmation is the point where structured interpretation becomes visible to the user.
71. Edit Before Creation
What if the user notices:
€120,000
should actually be:
€125,000
?
They should not need to create the wrong record and fix it later.
The confirmation workflow should support returning to the draft.
For example:
[Edit] [Cancel] [Create Opportunity]
72. Editing Invalidates the Prepared Mutation
This is important.
Once:
MutationRequest
has been prepared for:
€120,000
and the user changes the draft to:
€125,000
the old MutationRequest must no longer be executable.
The prepared action no longer matches the draft.
73. Mutation Request Supersession
When a prepared draft changes:
old MutationRequest→ cancelled or superseded
Then a new:
prepare_opportunity_creation
must create a fresh confirmation.
This ensures the user confirms exactly what will be executed.
74. Execute Creation Tool
Introduce:
create_opportunity
as the execution capability.
But like Part 18, it should not accept arbitrary record fields.
Input:
{ "mutation_request_id": "..."}
That is all.
75. Why Not Accept Opportunity Data Directly?
Avoid:
{ "company_id": "...", "name": "...", "amount": "...", "confirmed": true}
because the client could modify the record after confirmation.
Instead:
MutationRequest
contains the exact confirmed proposed record.
The execution tool merely references it.
76. Execution Flow
create_opportunity performs:
Load MutationRequest ↓Verify Tenant ↓Verify User ↓Verify Permission ↓Verify Pending ↓Verify Not Expired ↓Load Draft ↓Verify Draft Still Matches ↓Revalidate Company ↓Revalidate Fields ↓Recheck Duplicate Policy ↓Create Opportunity ↓Create AuditEvent ↓Mark Mutation Executed ↓Mark Draft Converted ↓Commit
Again, validation happens at execution time.
77. Why Revalidate the Company?
The Company may have been:
deletedarchivedmovedmade unavailable
between draft preparation and execution.
The fact that it existed earlier does not guarantee creation is still valid.
78. Why Revalidate the Draft?
We need to ensure:
confirmed values
still correspond to the draft state.
If the draft changed after preparation, reject the old MutationRequest.
Never create something different from what the user confirmed.
79. Transaction Boundary
The creation transaction should include:
BEGINcreate Opportunitycreate AuditEventmark MutationRequest executedmark OpportunityDraft convertedCOMMIT
If any required step fails:
ROLLBACK
No partial business state should remain.
80. Generate Opportunity ID Server-Side
The new:
opportunity.id
is generated inside Quorentra.
Neither ChatGPT nor the widget chooses it.
The authoritative result returns it afterward.
81. Initial Version
New Opportunities begin with:
version = 1
This immediately makes them compatible with the optimistic concurrency architecture from Part 18.
82. Creation Audit Event
Create:
action =opportunity.created
The event records:
organization_idactor_user_identity_idnew_valuesinvocation_sourcemutation_request_idcreated_at
There are no old Opportunity values because the record did not previously exist.
83. Example Audit Event
Conceptually:
{ "action": "opportunity.created", "entity_type": "opportunity", "entity_id": "...", "new_values": { "company_id": "...", "name": "Microsoft Copilot Deployment", "amount": "120000.00", "currency": "EUR", "stage": "qualification", "probability": 20, "expected_close_date": "2026-11-30" }, "invocation_source": "chatgpt_model"}
Again, avoid storing unnecessary sensitive information.
84. Authoritative Result
After commit, return:
{ "success": true, "mutation_request_id": "...", "opportunity": { "id": "...", "company_id": "...", "company_name": "Contoso", "name": "Microsoft Copilot Deployment", "amount": "120000.00", "currency": "EUR", "stage": "qualification", "probability": 20, "expected_close_date": "2026-11-30", "version": 1 }}
This is the record ChatGPT and the UI should trust.
85. Never Announce Creation Early
ChatGPT must not say:
I’ve created the opportunity.
after:
Draft complete
or:
Confirmation shown
It can only say that after:
create_opportunity
returns successful authoritative data.
86. Success UI
After successful creation:
┌────────────────────────────────────────────┐│ QUORENTRA ││ Opportunity Created ││ ││ Microsoft Copilot Deployment ││ Contoso ││ ││ €120,000 ││ Qualification · 20% ││ ││ Expected Close ││ 30 November 2026 ││ ││ [View Opportunity] │└────────────────────────────────────────────┘
This gives the user immediate confirmation and navigation.
87. Refresh Affected Views
Creating an Opportunity affects:
Opportunity ListPipeline SummaryStage ListsCompany Opportunity Count
At minimum, mark:
pipelineopportunity list
as stale.
Reload them when the user returns.
88. Pipeline Changes Immediately
Unlike Part 18’s stage-only update, creating an Opportunity directly changes:
Open Opportunity CountTotal PipelineWeighted Pipeline
Therefore the old pipeline summary is definitely stale.
It must not remain presented as current indefinitely.
89. Example Pipeline Change
Before:
4 Opportunities€385,000 Total€161,000 Weighted
Create:
Microsoft Copilot Deployment€120,00020%
After:
5 Opportunities€505,000 Total€185,000 Weighted
because:
€120,000 × 20% = €24,000
and:
€161,000 + €24,000= €185,000
The backend remains responsible for those calculations.
90. ChatGPT Follow-Up
After creation, ChatGPT can say:
Microsoft Copilot Deployment has been created for Contoso at €120,000, with an expected close date of November 30, 2026.
The user can then ask:
Show me the updated pipeline.
Quorentra retrieves fresh authoritative data.
91. One-Shot Creation
The conversational draft architecture does not mean every creation requires several turns.
Suppose the user says:
Create a €120,000 Microsoft Copilot Deployment opportunity for Contoso, expected to close November 30, 2026.
ChatGPT extracts all required information.
Quorentra creates a draft.
The draft immediately returns:
missing_fields = []
Then Quorentra can proceed directly to preparation and confirmation.
92. Best-Case Workflow
User gives complete intent ↓Create Draft ↓Draft Complete ↓Prepare Creation ↓Confirmation ↓Create
Only one user confirmation is required.
This makes conversational CRM faster than traditional form entry.
93. Incomplete Workflow
If information is missing:
User gives partial intent ↓Create Draft ↓Missing Fields ↓Ask User ↓Update Draft ↓Still Missing? ┌───┴────┐ Yes No │ │ └─loop ▼ Prepare
The same architecture handles both simple and incomplete requests.
94. User Corrections
Suppose the user says:
Actually make that €125,000.
ChatGPT updates:
amount
in the draft.
It does not create a second draft unless necessary.
Conversational correction is one of the advantages of this model.
95. Explicit Field Provenance
A useful future enhancement is tracking where each draft value came from.
For example:
namesource = useramountsource = usercurrencysource = organization_defaultstagesource = business_defaultprobabilitysource = stage_default
This can improve transparency and debugging.
96. Should We Add Provenance Now?
A lightweight provenance map is worthwhile.
Conceptually:
{ "field_sources": { "name": "user", "amount": "user", "currency": "organization_default", "stage": "business_default", "probability": "stage_default", "expected_close_date": "user" }}
This makes AI-assisted data capture more explainable.
97. Why Provenance Matters
Later we may want to know:
Which values did the user explicitly provide?Which were defaults?Which were extracted from CRM data?Which were suggested by AI?Which were confirmed?
For enterprise AI systems, provenance becomes increasingly valuable.
98. Do Not Treat AI Suggestions as Facts
Later, ChatGPT might suggest:
Based on similar opportunities, you may want to use a 40% probability.
That can be helpful.
But if accepted, the system should distinguish:
AI suggested+User accepted
from:
AI silently inserted
Part 19 avoids AI-generated business values entirely unless explicitly accepted.
99. Description Field
description can remain optional.
If the user supplies:
This is a 500-seat Copilot deployment including adoption services.
ChatGPT can capture that as:
description
If no description is supplied:
NULL
is acceptable.
Do not generate marketing prose just to fill the field.
100. Validation Layer
The draft service should validate each supplied value as soon as possible.
For example:
name→ length validationamount→ Decimal validationcurrency→ supported currency validationstage→ OpportunityStage validationprobability→ 0–100expected_close_date→ valid date
Early validation makes conversational correction easier.
101. Invalid Amount Example
User:
The opportunity is worth minus €20,000.
If negative Opportunities are not supported:
amount = -20000
fails.
ChatGPT can explain:
Quorentra requires the opportunity amount to be zero or greater. What amount should I use?
The backend produced the rule.
ChatGPT produced the conversational explanation.
102. Invalid Probability
User:
Set the probability to 130%.
Quorentra rejects it because:
0 <= probability <= 100
ChatGPT asks for a valid value.
Again, the model does not decide the business rule.
103. Unsupported Currency
If Quorentra only supports configured currencies and receives:
XYZ
the server returns a structured validation error.
The user can correct it.
104. Past Close Date
Should:
expected_close_date
be allowed in the past?
For open Opportunities, probably not.
Define this as a business rule.
For example:
new open Opportunityexpected_close_date >= today
unless importing historical data through another workflow.
105. Creation Rules Belong in the Domain
The ChatGPT interface should not contain:
if close_date < today
as the authoritative rule.
The application/domain layer validates it.
This ensures REST and future integrations behave identically.
106. Draft Validation Errors
Return structured errors such as:
{ "field": "expected_close_date", "code": "DATE_IN_PAST", "message": "Expected close date must not be in the past."}
ChatGPT can turn this into a natural follow-up question.
107. Field-Level Errors Are Valuable
Instead of:
Invalid draft
return:
amount → requiredexpected_close_date → requiredprobability → invalid
This makes both UI and conversational repair much easier.
108. Draft UI
Although the workflow is conversation-first, the widget can also show the current draft.
For example:
┌────────────────────────────────────────────┐│ QUORENTRA ││ New Opportunity Draft ││ ││ Company Contoso ││ Name Microsoft Copilot Deployment││ Amount — ││ Currency EUR ││ Stage Qualification ││ Probability 20% ││ Close Date — ││ ││ Missing: Amount, Close Date │└────────────────────────────────────────────┘
This gives the user visual grounding.
109. Conversation and Widget Can Cooperate
The user might type:
€120,000, closing November 30.
ChatGPT updates the draft.
The widget automatically reflects:
Amount €120,000Close Date 30 Nov 2026
This is an excellent example of:
Conversation+Structured UI
working together.
110. Do Not Build a Full Form Yet
We could make every draft field editable directly in React.
But that would move us back toward a conventional CRM form.
For the MVP, keep the widget focused on:
ReviewProgressConfirmation
while conversation handles most data entry.
Later we can add selective inline editing where useful.
111. Draft Tool Logging
Log:
tool_namedraft_idorganization_iduser_idfields_changedstatusduration_ms
Avoid logging entire descriptions or sensitive business content unless operationally necessary.
112. Creation Mutation Logging
For final execution, log:
mutation_request_identity_idactor_user_idorganization_idinvocation_sourceresultduration_ms
The AuditEvent remains the authoritative business audit trail.
Operational logs and audit logs have different purposes.
113. Draft Repository Tests
Test:
create tenant-scoped draftload own draftcannot load other tenant draftcannot load another user's draftupdate draftexpire draftcancel draft
114. Default Tests
Verify:
stage defaults to qualificationprobability defaults correctlycurrency defaults from organizationexplicit stage overrides defaultexplicit probability overrides defaultexplicit currency overrides default
provided those explicit values are permitted.
115. Missing-Field Tests
Create a draft with only:
company_id
Expected:
missing_fields:nameamountexpected_close_date
Then add:
name
Expected:
missing_fields:amountexpected_close_date
Then complete the draft.
Expected:
missing_fields = []status = complete
116. Company Resolution Tests
Test:
exact Company matchCompany not foundambiguous Company matchcross-tenant Company inaccessiblearchived Company rejected if policy requires
These tests are important because entity resolution is part of creation safety.
117. Duplicate Tests
Create existing:
ContosoMicrosoft Copilot Deployment
Then prepare another draft with the same normalized name.
Expected:
potential duplicate warning
according to policy.
Do not silently create duplicates without awareness.
118. Permission Tests
Viewer:
create draft → deniedprepare creation → deniedexecute creation → denied
Authorized Member:
create draft → allowedprepare → allowedexecute → allowed
119. Permission Change Test
Authorized user creates and prepares a draft.
Before confirmation, permission changes to Viewer.
Execution:
denied
No Opportunity is created.
Again, authorization is checked at execution.
120. Tenant Isolation Tests
Tenant A must not:
use Tenant B Companyread Tenant B draftupdate Tenant B draftprepare Tenant B draftexecute Tenant B MutationRequest
Test every boundary.
121. Draft Ownership Tests
User A creates a draft.
User B in the same organization should not automatically be able to mutate or execute it.
Our MVP keeps drafts user-owned.
Collaborative drafts can come later.
122. Preparation Tests
Verify:
incomplete draft cannot prepareexpired draft cannot preparecancelled draft cannot prepareinvalid Company cannot preparecomplete valid draft can prepareprepared MutationRequest matches exact draft
123. Edit-After-Prepare Test
Prepare:
Amount = €120,000
Then change the draft:
Amount = €125,000
Expected:
old MutationRequest invalidated
A new preparation is required.
This test protects confirmation integrity.
124. Execution Tests
Successful execution must:
create exactly one Opportunityassign active tenantassign resolved Companyuse confirmed valuesset version = 1create AuditEventmark MutationRequest executedmark Draft convertedreturn authoritative Opportunity
125. Idempotency Test
Execute the same MutationRequest twice.
Expected:
one Opportunityone AuditEventone logical result
Never:
two duplicate Opportunities
This is particularly important for creation.
126. Transaction Failure Test
Simulate failure while creating AuditEvent.
Expected:
Opportunity creation rolled backMutationRequest not executedDraft not converted
The database remains consistent.
127. Company Deletion Race Test
Prepare creation for:
Contoso
Then make Contoso unavailable before execution.
Expected:
creation rejected
Do not create an Opportunity pointing at an invalid Company.
128. Duplicate Race
Two users may simultaneously prepare the same Opportunity.
If exact duplicates must be prohibited, enforce a suitable database/domain uniqueness strategy.
If duplicates are merely discouraged, warn but permit.
This is a product decision.
For the MVP, duplicate awareness without strict uniqueness is usually more practical.
129. Why Not Enforce Unique Opportunity Names?
A Company may legitimately have:
Microsoft 365 Migration
Opportunities in different years or business units.
Therefore:
company_id + opportunity_name
should not automatically become a database unique constraint.
Duplicate detection and uniqueness are not the same thing.
130. End-to-End Scenario 1
User:
Create a new opportunity for Contoso.
Expected:
Company resolvedDraft createdDefaults appliedMissing fields returned
No Opportunity exists yet.
131. Scenario 2: Complete the Draft
User:
Microsoft Copilot Deployment.
Then:
€120,000.
Then:
November 30, 2026.
Expected draft:
Company:ContosoName:Microsoft Copilot DeploymentAmount:€120,000Currency:EURStage:QualificationProbability:20%Expected Close:30 November 2026Status:Complete
132. Scenario 3: Prepare
Quorentra calls:
prepare_opportunity_creation
Expected:
MutationRequest createdConfirmation rendered
Still:
0 new Opportunity rows
133. Scenario 4: Cancel
Click:
Cancel
Expected:
No Opportunity createdMutationRequest cancelledDraft retained or cancelled according to policy
The user can safely abandon the operation.
134. Scenario 5: Confirm
Prepare again.
Click:
Create Opportunity
Expected:
1 Opportunity created1 AuditEvent createdDraft convertedMutationRequest executed
The success widget renders.
135. Scenario 6: Verify Pipeline
Ask:
Show my EUR pipeline.
The new Opportunity must be reflected in:
Open Opportunity CountTotal PipelineWeighted Pipeline
This proves integration with the existing CRM.
136. Scenario 7: Complete One-Shot Request
User:
Create a €75,000 Azure Landing Zone opportunity for Fabrikam closing December 15, 2026.
Expected:
Company = FabrikamName = Azure Landing ZoneAmount = 75000Currency = EURClose Date = 2026-12-15Stage = QualificationProbability = 20%
The draft is complete immediately.
Quorentra proceeds to confirmation.
137. Scenario 8: Ambiguous Company
User:
Create an opportunity for Contoso.
Database:
Contoso EuropeContoso Consulting
Expected:
No Company chosenNo final draft preparationUser clarification requested
Never select one arbitrarily.
138. Scenario 9: Missing Company
User:
Create an opportunity for Adventure Works.
No Company exists.
Expected:
Company not found
Quorentra can explain that the Company must first exist.
Later we will make Company creation conversational too.
139. Scenario 10: User Correction
User:
Create a €120,000 Copilot project for Contoso closing November 30.
Draft complete.
Then:
Actually make it €125,000.
Expected:
Draft amount updatedAny prepared mutation invalidatedNew confirmation required
This proves conversational correction works safely.
140. The Emerging Conversational Data Entry Pattern
We now have a reusable pattern:
Natural Language ↓Extract Known Facts ↓Resolve References ↓Create Structured Draft ↓Apply Deterministic Defaults ↓Detect Missing Fields ↓Ask User ↓Update Draft ↓Validate ↓Prepare ↓Confirm ↓Execute
This pattern will later work for:
CompaniesContactsTasksActivitiesMeetingsNotes
141. Why This Is Better Than Giving the Model CRUD
A naive AI CRM might expose:
create_record( table, fields)
and let the model decide what to send.
Quorentra instead exposes:
create_opportunity_draftupdate_opportunity_draftprepare_opportunity_creationcreate_opportunity
These capabilities encode business semantics.
That is more verbose architecturally.
It is also far safer.
142. Domain-Specific Tools Beat Generic CRUD
Compare:
create_recordupdate_recorddelete_record
with:
prepare_opportunity_creationcreate_opportunityupdate_opportunity_stage
The second group makes it easier to implement:
Business rulesPermissionsConfirmationAuditTool descriptionsTestingObservability
This is the direction Quorentra should continue taking.
143. ChatGPT Becomes an Intent Layer
At this stage, ChatGPT’s primary job is becoming clear.
It translates:
"Create a €120k Copilot deal for Contosoclosing at the end of November."
into:
Structured Business Intent
Quorentra then handles:
IdentityTenantPermissionsDefaultsValidationPersistenceAudit
This division of responsibility is powerful.
144. The CRM Database Remains Authoritative
Even though the user may experience the workflow almost entirely through ChatGPT, authoritative state still lives in:
PostgreSQL
not:
Conversation historyWidget stateModel memoryPrompt context
That separation makes the system dependable.
145. Drafts Are Application State
This deserves emphasis.
A draft is:
temporary application state
but still application state.
It belongs in Quorentra.
This is different from merely remembering that the user mentioned:
€120,000
earlier in a conversation.
146. Future Draft Reuse
Later, users may be able to say:
Continue the Contoso opportunity I started yesterday.
Because drafts are persisted, Quorentra can find:
incomplete OpportunityDraft
and continue.
That would be difficult if all state existed only in conversation history.
147. Future Draft List
We may eventually expose:
list_my_opportunity_drafts
so ChatGPT can answer:
Do I have any unfinished opportunities?
But that is not required for Part 19.
148. Future AI Assistance
Once the basic workflow is safe, AI can provide optional assistance.
For example:
Based on the opportunity description, would you like me to suggest a concise opportunity name?
or:
Similar opportunities typically use the Proposal stage. Would you like to change it?
The key phrase is:
Would you like...
AI suggestions remain proposals until accepted.
149. Future CRM Intelligence
Eventually Quorentra may calculate:
Recommended probabilityExpected close riskOpportunity qualityNext best actionStakeholder coverageCompetitive risk
But these belong to later intelligence modules.
Part 19 deliberately keeps creation deterministic and transparent.
150. Version Update
Part 19 adds conversational record creation.
Update:
app/core/constants.py
from:
APP_VERSION = "0.5.0"
to:
APP_VERSION = "0.6.0"
Quorentra now supports both:
Governed CRM updates
and:
Governed CRM creation
from ChatGPT.
151. Quorentra 0.6.0
Our MVP status becomes:
Platform├── FastAPI ✓├── PostgreSQL ✓├── SQLAlchemy ✓└── Alembic ✓Identity├── Organizations ✓├── Users ✓├── Memberships ✓├── Authentication ✓└── JWT ✓Security├── TenantContext ✓├── Tenant Isolation ✓├── RBAC ✓├── Read Permissions ✓├── Create Permissions ✓└── Update Permissions ✓CRM├── Companies ✓├── Contacts ✓└── Opportunities ✓Sales├── Pipeline ✓├── Opportunity Stages ✓├── Probability ✓└── Weighted Pipeline ✓Interfaces├── REST ✓├── MCP ✓└── ChatGPT ✓ChatGPT UI├── Pipeline Widget ✓├── Opportunity List ✓├── Opportunity Detail ✓├── Mutation Confirmation ✓├── Creation Confirmation ✓└── Creation Result ✓Mutation Governance├── Mutation Requests ✓├── Confirmation ✓├── Expiry ✓├── Cancellation ✓├── Idempotency ✓├── Transactions ✓├── Optimistic Concurrency ✓└── Audit Events ✓Conversational Creation├── Opportunity Drafts ✓├── Company Resolution ✓├── Missing-Field Detection ✓├── Business Defaults ✓├── Draft Validation ✓├── Field Provenance ✓├── Duplicate Awareness ✓├── Multi-Turn Completion ✓├── Draft Correction ✓├── Prepared Creation ✓└── Safe Opportunity Creation ✓ChatGPT CRM Operations├── Read Pipeline ✓├── Read Opportunities ✓├── Filter Opportunities ✓├── View Opportunity ✓├── Update Opportunity Stage ✓├── Create Opportunity ✓├── Create Company -├── Create Contact -├── Create Task -└── Create Activity -Activities -Tasks -AI Intelligence -
152. Acceptance Criteria
Part 19 is complete when:
✓ Part 18 regression suite remains green✓ opportunities.create permission exists✓ Viewer cannot create Opportunities✓ authorized role can create drafts✓ OpportunityDraft model exists✓ OpportunityDraft migration applies✓ drafts are tenant-scoped✓ drafts are user-scoped✓ drafts expire✓ drafts support incomplete fields✓ create_opportunity_draft exists✓ update_opportunity_draft exists✓ draft tools do not create Opportunities✓ Company references resolve server-side✓ exact Company match works✓ ambiguous Company is not guessed✓ missing Company is not auto-created✓ cross-tenant Company cannot be used✓ organization default currency is applied✓ default stage is applied✓ stage probability default is applied✓ explicit valid values override defaults✓ defaults are server-side✓ required fields are server-defined✓ missing_fields is returned✓ incomplete draft remains incomplete✓ complete draft becomes complete✓ amount uses Decimal✓ amount validation works✓ currency normalization works✓ probability validation works✓ date validation works✓ field-level errors are structured✓ field provenance exists✓ user values are distinguishable from defaults✓ AI does not silently invent required business facts✓ duplicate awareness exists✓ potential duplicates can be surfaced✓ duplicate awareness remains tenant-scoped✓ prepare_opportunity_creation exists✓ incomplete draft cannot prepare✓ complete valid draft can prepare✓ preparation does not create Opportunity✓ MutationRequest contains exact proposed values✓ confirmation expiry exists✓ confirmation UI displays Company✓ confirmation UI displays Opportunity name✓ confirmation UI displays amount✓ confirmation UI displays currency✓ confirmation UI displays stage✓ confirmation UI displays probability✓ confirmation UI displays expected close date✓ business defaults are visible✓ CRM side effect is explicit✓ Cancel does not create Opportunity✓ editing after prepare invalidates old confirmation✓ new confirmation is required after draft changes✓ create_opportunity accepts mutation_request_id✓ arbitrary client fields cannot replace confirmed values✓ authentication is rechecked✓ tenant is rechecked✓ permission is rechecked✓ MutationRequest status is rechecked✓ expiry is rechecked✓ draft is rechecked✓ Company is rechecked✓ values are revalidated✓ Opportunity is created transactionally✓ version starts at 1✓ AuditEvent is created transactionally✓ MutationRequest becomes executed✓ OpportunityDraft becomes converted✓ failure rolls everything back✓ duplicate execution creates only one Opportunity✓ duplicate execution creates only one AuditEvent✓ retry returns the original logical result✓ authoritative Opportunity is returned✓ ChatGPT reports creation only after server success✓ success UI uses authoritative result✓ Opportunity list is invalidated after creation✓ Pipeline summary is invalidated after creation✓ fresh pipeline includes new Opportunity✓ weighted pipeline reflects new Opportunity✓ one-shot complete creation works✓ multi-turn incomplete creation works✓ user correction works✓ ambiguous Company workflow works✓ missing Company workflow works✓ permission-change-before-execution is respected✓ cross-tenant creation attempts fail✓ Quorentra reports version 0.6.0
Most importantly:
A user can now describe a sales opportunity naturally, provide missing information conversationally, review the exact structured record Quorentra intends to create, explicitly confirm it, and create an authoritative tenant-safe CRM record without giving ChatGPT unrestricted CRUD authority.
153. What We Have Achieved
The Quorentra creation path now looks like:
Natural Language ↓ChatGPT ↓Extract Explicit Facts ↓Quorentra Draft ↓Resolve CRM References ↓Apply Business Defaults ↓Detect Missing Fields ↓Conversation ↓Complete Draft ↓Validate ↓Prepare Mutation ↓Confirmation ↓Create Opportunity ↓Audit ↓PostgreSQL ↓Authoritative Result
This is a major step toward a genuinely ChatGPT-native CRM.
154. The Difference From a Traditional CRM
Traditional CRM creation is primarily:
Navigate ↓Open Form ↓Populate Fields ↓Save
Quorentra can support:
Describe Intent ↓Review Structured Interpretation ↓Confirm
The underlying CRM discipline does not disappear.
In fact, it becomes more important.
Natural language is flexible.
Business data must remain precise.
155. Conversation Replaces Navigation, Not Governance
This distinction captures the architecture well:
Quorentra uses conversation to reduce navigation and data-entry friction, not to remove validation, authorization, or business rules.
The user should experience less CRM bureaucracy.
The backend should become more rigorous, not less.
156. The Next Missing Piece
We can now:
Create OpportunityUpdate Opportunity StageRead OpportunityList OpportunitiesAnalyze Pipeline
But the Opportunity must belong to an existing:
Company
If the Company does not exist, the workflow stops.
For a useful CRM MVP, that is the next limitation to remove.
157. Example
User:
Create a €90,000 Azure migration opportunity for Adventure Works.
Quorentra searches Companies.
Result:
Adventure Worksnot found
Today we must stop.
But the natural next experience is:
Adventure Works isn’t in Quorentra yet. Would you like me to create the company first?
That introduces chained business operations.
158. Company Creation Has Different Fields
A Company might require:
NameWebsiteIndustryPhoneAddressCountryNotes
But unlike an Opportunity, perhaps only:
Name
is mandatory for the MVP.
This gives us an opportunity to design a much lighter conversational creation workflow.
159. Chained Operations
Once Company creation exists, the original request:
Create a €90,000 Azure migration opportunity for Adventure Works.
could become:
Resolve Company ↓Not Found ↓Prepare Company Creation ↓Confirm ↓Create Company ↓Resume Opportunity Draft ↓Prepare Opportunity ↓Confirm ↓Create Opportunity
That is the beginning of multi-step conversational CRM workflows.
160. Next Article
Creating Companies from ChatGPT — Entity Resolution, Duplicate Prevention, Conversational Creation, and Chained CRM Workflows
We will introduce:
Company creation permissionCompanyDraftCompany name normalizationExact-match detectionDuplicate detectionWebsite/domain normalizationOptional Company enrichmentMissing-field detectionBusiness validationCompany creation confirmationPrepared Company mutationsIdempotent Company creationCreation audit eventsCompany result widgetsTenant isolationRBACResume-after-create contextOpportunity draft dependency handlingChained Company → Opportunity workflow
The target interaction will be:
User:"Create a €90,000 Azure migrationopportunity for Adventure Works."
Quorentra determines:
Adventure Worksnot found
and asks:
Adventure Works isn't currently in Quorentra.Would you like to create the company first?
After confirmation:
Create Company ↓Company ID ↓Resume Opportunity Draft ↓€90,000 Azure Migration ↓Opportunity Confirmation ↓Create Opportunity
The resulting experience becomes:
Natural Language Request ↓Resolve Dependencies ↓Create Missing CRM Entity ↓Resume Original Intent ↓Create Dependent CRM Entity ↓Audit Both Operations
That will be our first multi-entity conversational business workflow in Quorentra.
And it will move us one step closer to the central vision of the series:
A modular CRM where ChatGPT provides the conversational operating layer, while Quorentra remains the secure, deterministic, tenant-aware system of record underneath.