Quickly Creating Cases from the Service Console using a Utility Action (Aura + LWC)

A step-by-step guide to building a Service Console utility action (Aura wrapper + LWC) that lets agents quickly capture case details while a contact record loads — improving efficiency and data accuracy.

Overview

Call center agents need fast, reliable ways to capture case details while still on a call — often before the related Contact record finishes loading. This solution uses an Aura component wrapper (to guarantee the recordId) and a Lightning Web Component (LWC) launched as a utility action in the Service Console. It captures case data immediately and associates the case with a Contact later when the record becomes available.

Use case and benefits

  • Works well with dual-monitor setups where agents use a utility window while the main console loads records.
  • Enables agents to start capturing case details immediately, preventing lost data during latency.
  • Prevents incorrect associations by validating the recordId and showing clear error messages when appropriate.
  • Improves speed, accuracy, and overall agent experience.

Why use Aura as a wrapper

Although LWC is preferred for modern components, there are scenarios inside the console where recordId may not be reliably available directly to an LWC. Wrapping the LWC with a small Aura component that implements force:hasRecordId ensures the recordId is consistently captured and passed into the LWC.

Solution architecture

  • Aura component (utility item) — captures recordId and passes it to the LWC.
  • LWC — UI for entering case details, wiring to getRecord for Contact fields, handling save/minimize, and calling Apex to insert the Case.
  • Apex controller — simple @AuraEnabled method to insert Case records.

Key implementation snippets

Aura wrapper (markup)

<aura:component
implements="force:hasRecordId,flexipage:availableForAllPageTypes,lightning:utilityItem">
    <aura:attribute name="recordId" type="String" />
    <aura:handler name="init" value="{!this}" action="{!c.doInit}" />
<c:createCaseUtility contactId="{!v.recordId}"/>
</aura:component>

Aura controller (JS)

({
    doInit: function (component, event, helper) {
        var recordId = component.get("v.recordId");
        if (!recordId) {
            console.warn("Record ID is undefined. Ensure the utility is launched from a record context.");
        } else {
            console.log("Record ID retrieved:", recordId);
        }
    }
});

LWC template (HTML)

<template>
    <lightning-card>
        <div class="slds-m-left_small slds-m-right_small">
            ...
            <!-- Save button -->
            <lightning-button class="slds-m-left_small"
                label="Save"
                variant="brand"
                onclick={handleSave}>
            </lightning-button>
        </div>
    </lightning-card>
</template>

LWC JavaScript (key parts)

import { LightningElement, wire, api } from 'lwc';
import createCase from '@salesforce/apex/CreateCaseController.createCase';
import { ShowToastEvent } from 'lightning/platformShowToastEvent';
import { minimize, EnclosingUtilityId } from 'lightning/platformUtilityBarApi';
import { getRecord } from 'lightning/uiRecordApi';
const CONTACT_FIELDS = ['Contact.Name','Contact.Phone','Contact.AccountId'];

export default class CreateCaseUtility extends LightningElement {
    // properties
    currentRecordId;
    @api
    set contactId(value) {
        if (value !== this.currentRecordId) {
            this.currentRecordId = value;
        }
    }
    get contactId() { return this.currentRecordId; }

    @wire(getRecord, { recordId: '$currentRecordId', fields: CONTACT_FIELDS })
    wiredCase({ error, data }) {
        if (data) {
            // map fields
        } else if (error) {
            console.error('Error fetching Contact:', error);
        }
    }

    handleSave() {
        if (!this.currentRecordId || !this.currentRecordId.startsWith('003')) {
            this.showToast('Case Create','Case should be created from Contact','error');
            return;
        }
        const caseRecord = {
            Type: this.caseType,
            Status: this.caseStatus,
            Origin: this.caseOrigin,
            Description: this.description,
            ContactId: this.currentRecordId,
            Reason: this.caseReason,
            AccountId: this.accountId,
            ContactPhone: this.contactPhone,
            Subject: this.subject
        };
        createCase({ cse: caseRecord })
            .then(() => { this.showToast('Success', 'Case created successfully!', 'success'); this.resetForm(); })
            .catch((error) => { this.showToast('Error', 'Error creating case: ' + error.body.message, 'error'); });
    }
}

Apex (CreateCaseController.cls)

public class CreateCaseController {
    @AuraEnabled public static void createCase(Case cse) {
        insert cse;
    }
}

Best practices and notes

  • Validate the recordId server-side or client-side to avoid creating orphaned cases. In this example the code checks the 003 prefix to ensure it’s a Contact.
  • Prefer setting a default RecordTypeId for the Case if your org uses multiple record types.
  • Sanitize field inputs and add required-field checks before calling Apex.
  • If you need to support email-to-case or bulk creation, build a separate pathway to delegate bulk operations — keep the utility focused on single, quick case capture.
  • Use platform events or pub/sub (Lightning Message Service) if other console components should react to the new Case.

How this helps admins, developers and business users

Admins can deploy this utility to improve agent productivity with minimal changes to existing console layouts. Developers can extend the LWC and Apex to include additional validation, assignment rules, and automation. For business users, the net result is faster case resolution, fewer data errors, and a better customer experience.

Ready to implement? Use the code snippets above as a starting point — wrap the LWC with an Aura utility item, wire getRecord for Contact fields, and add small validation guards to protect data integrity.