Extracting Key Points, Narrative, Sources, and Reference Metadata

In my previous AI blog post, we covered the very basics of submitting prompts via the OpenAI API from Salesforce. This post goes a bit deeper into the processing of the payload that OpenAI gives back from the /responses endpoint and persisting it in Salesforce with traceability, governance, and repeatability.

We focus specifically on:

  • Requesting structured output from the model
  • Parsing the Responses payload safely in Apex
  • Accessing key points, narrative summaries, sources, and reference metadata
  • Storing and rendering results in Salesforce without brittle parsing logic

High-Level Architectural Overview

A clean integration separates responsibilities. Anything else becomes technical debt immediately.

Recommended layers:

  1. Configuration
    • Named Credential for OpenAI
    • Custom Metadata for model, temperature, and limits
  2. Transport
    • Apex service – responsible only for HTTP callouts and errors
  3. Domain
    • Request builders and response DTOs
    • Payload parsing and validation
  4. Persistence & UI
    • Store structured results in Salesforce
    • Render via LWC, Flow, or Experience Cloud

Do not put all of this into one Apex class. That’s amateur hour.


Named Credential Setup

Use an External Credential + Named Credential.

  • Base URL: https://api.openai.com
  • Auth: Bearer token stored securely
  • Endpoint usage in Apex: callout:OpenAI_NC/v1/responses

This avoids security issues, hardcoded secrets, Remote Site Settings, and deployment nightmares.

** This cannot be emphasized enough… Use Named Credentials in Salesforce!


Why the Responses Endpoint Matters

The Responses API is designed for:

  • Structured output
  • Tool-based responses
  • Multi-part content
  • Usage metadata

If you’re still calling legacy chat endpoints and parsing text blobs, you’re behind.


What You Should Ask the Model to Return

For most transactions, you can return

  • Key points
  • Narrative
  • Sources
  • Reference payload

You do not get this reliably by asking politely in natural language.

You get it by enforcing JSON schema output.

Target Output Shape

This is the structure you want Salesforce to receive and process:

{
  "keyPoints": ["...", "..."],
  "narrative": "...",
  "sources": [
    {
      "title": "...",
      "url": "...",
      "publisher": "...",
      "snippet": "...",
      "confidence": 0.92
    }
  ],
  "references": {
    "model": "gpt-4.1-mini",
    "responseId": "resp-abc123",
    "usage": {
      "inputTokens": 1200,
      "outputTokens": 350,
      "totalTokens": 1550
    }
  }
}

This is what makes downstream processing sane.


Enforce Structured Output

The Responses API allows you to specify a response_format using a strict JSON schema.

If you don’t:

  • The model may return prose
  • You’ll end up regex-parsing text
  • Your integration will eventually break

Key Rules

  • Set "strict": true
  • Disable additional properties
  • Make required fields explicit

If the model cannot comply, it fails fast—which is exactly what you want in a system integration.


Making the Callout from Apex

At a high level:

  1. Build a request with:
    • Model
    • Temperature
    • Persona
    • Sentiment
    • Token limits
    • Messages (system + user)
    • JSON schema response format
  2. POST to /v1/responses
  3. Deserialize safely
  4. Extract your structured payload from the output array

The Responses payload is not a flat object. You must walk it.


Understanding the Responses Payload

At runtime, the API returns:

  • Top-level metadata (id, model, usage)
  • An output array
  • Each output item contains content
  • Each content item may contain text, tool output, or structured JSON

When you enforce JSON schema output, your structured result typically appears as JSON text embedded in the output content.

That means:

  • You deserialize the outer payload loosely
  • Then attempt to parse inner text as JSON
  • If it parses cleanly, you’ve found your result

This is intentional. It allows the API to support multiple content types without breaking clients.


Extracting Key Points and Narrative

Once parsed, access is trivial:

  • Key points
    Use as bullet lists, highlights, or executive summaries.
  • Narrative
    This is your human-readable summary—store it as Long Text and render it directly.

Do not concatenate these back into prose. Keep them structured.


Handling Sources Correctly

If you ask the model for sources without providing a corpus, it will hallucinate.

That’s not an OpenAI bug— it is a design flaw.

Valid Source Strategies

  • Salesforce records you pass into the prompt (record URLs)
  • Knowledge articles
  • ContentDocument links
  • Data Cloud or external datasets you retrieved yourself

What to Tell the Model

Be explicit:

Only cite sources provided in the input.
If no source supports a claim, omit it.

Then store sources as structured objects, not formatted text.


Sources vs Reference Payload

These serve different purposes and should be stored separately.

Sources

Reader-facing

  • URLs
  • Titles
  • Snippets
  • Confidence or relevance

These justify the narrative.

Reference Payload

System-facing

  • Response ID
  • Model used
  • Token usage
  • Raw response JSON (optional but recommended)

This enables:

  • Auditing
  • Cost tracking
  • Support diagnostics
  • Model performance analysis

Conflating the two is sloppy design.


What you should persist in Salesforce

At minimum, store:

  • Narrative (Long Text)
  • Key points (JSON or newline-delimited)
  • Sources (JSON)
  • Response ID
  • Model
  • Input / output / total tokens
  • Raw response payload (optional, but extremely useful)

Whether this lives in AI_Response__c, a related object, or a platform event depends on your use case—but skipping persistence entirely is short-sighted.


Common Failure Modes

Output isn’t JSON

  • Fix: strict schema + retry once with lower temperature

Sources are nonsense

  • Fix: restrict citations to supplied context

Schema drift breaks parsing

  • Fix: deserialize top-level payload as untyped, map selectively

Token explosions

  • Fix: chunk data, summarize iteratively, cap max_output_tokens

No audit trail

  • Fix: store response ID, model, and usage every time

Where This Becomes Real

Once you’ve implemented this correctly, you can build:

  • Executive summaries with cited sources
  • Sales or service insights with traceable evidence
  • Knowledge synthesis with governance
  • AppExchange-grade AI features that won’t embarrass you later

This is the difference between “we added AI” and “we built an AI-powered system.”


Final Take

Calling the OpenAI API from Salesforce is easy.
Processing the Responses payload correctly is the actual work.

If you:

  • Enforce structured output
  • Parse defensively
  • Separate sources from reference metadata
  • Persist results with traceability

You end up with something enterprise-ready, where you can answer the big question that will always be asked… “where did this come from?

We would love to hear your comments!

Trending