Skip to main content

Insights Custom Instructions

This package contains React hooks and utilities for managing Sales Strategist custom instructions through the Analytics API.

Overview​

Custom instructions allow administrators to configure guidance at three hierarchical levels:

  • Team: Instructions that apply to all team members
  • Group: Instructions specific to user groups
  • Individual: Instructions for specific users

The resolution follows a priority hierarchy: Individual > Group > Team

Installation​

pnpm add @shared/custom-instructions

Quick Start​

import {
useCustomInstructions,
useCreateCustomInstruction,
} from '@shared/custom-instructions'

function MyComponent() {
// List instructions
const { data, isLoading } = useCustomInstructions()

// Create instruction
const { mutate: createInstruction } = useCreateCustomInstruction()

// Use them in your component
// ...
}

Available Hooks​

Query Hooks (Data Fetching)​

HookPurposeParameters
useCustomInstructionsFetch paginated list of instructionsparams?: ListCustomInstructionsParams
useAllCustomInstructionsFetch ALL instructions across all pagesparams?: Omit<ListCustomInstructionsParams, 'page' \| 'per_page'>
useCustomInstructionFetch single instruction by IDid: number
useResolvedInstructionsFetch resolved instructions for current userparams: ResolvedInstructionsParams

Mutation Hooks (Data Modification)​

HookPurposeParameters
useCreateCustomInstructionCreate new instructionCreateCustomInstructionParams
useUpdateCustomInstructionUpdate existing instruction{ id: number, params: UpdateCustomInstructionParams }
useDeleteCustomInstructionDelete single instructionid: number
useBulkDeleteCustomInstructionsDelete multiple instructions{ custom_instruction_ids: number[] }

Architecture​

File Structure​

All implementation files are organized in a flat structure at src/ root, following a consistent naming pattern:

src/
├── customInstructions.hooks.ts # React Query hooks
├── customInstructions.api.ts # API request functions
├── customInstructions.constants.ts # Constants and configurations
├── customInstructions.types.ts # TypeScript types and interfaces
├── customInstructions.spec.ts # Unit tests
└── index.ts # Public exports

API Endpoints​

All hooks interact with the following endpoints:

  • GET /v2/sales_strategist/custom_instructions - List instructions
  • POST /v2/sales_strategist/custom_instructions - Create instruction
  • GET /v2/sales_strategist/custom_instructions/:id - Get single instruction
  • PUT /v2/sales_strategist/custom_instructions/:id - Update instruction
  • DELETE /v2/sales_strategist/custom_instructions/:id - Delete instruction
  • POST /v2/sales_strategist/custom_instructions/bulk_delete_instructions - Bulk delete
  • GET /v2/sales_strategist/custom_instructions/resolved - Get resolved instructions

Usage Examples​

List Custom Instructions​

import { useCustomInstructions } from '@shared/custom-instructions'

function CustomInstructionsList() {
const { data, isLoading, error } = useCustomInstructions({
per_page: 25,
page: 1,
sort_by: 'created_at',
sort_direction: 'desc'
})

if (isLoading) return <div>Loading...</div>
if (error) return <div>Error loading instructions</div>

return (
<div>
{data?.data.map(instruction => (
<div key={instruction.id}>{instruction.instructions}</div>
))}
</div>
)
}

Get All Custom Instructions (All Pages)​

Automatically fetches all pages when you need all instructions (e.g., for validation):

import { useAllCustomInstructions } from '@shared/custom-instructions'

function InstructionsValidation() {
const { data, isLoading, totalCount } = useAllCustomInstructions({
sort_by: 'created_at',
sort_direction: 'desc'
})

if (isLoading) return <div>Loading all instructions...</div>

// data is an array with ALL instructions across all pages
const teamInstructionExists = data?.some(
instruction => instruction.instruction_type === 'team'
) ?? false

return (
<div>
<p>Total instructions: {totalCount}</p>
<p>Team instruction exists: {teamInstructionExists ? 'Yes' : 'No'}</p>
</div>
)
}

Get Single Custom Instruction​

import { useCustomInstruction } from '@shared/custom-instructions'

function CustomInstructionDetail({ id }: { id: number }) {
const { data, isLoading } = useCustomInstruction(id)

if (isLoading) return <div>Loading...</div>

return <div>{data?.instructions}</div>
}

Get Resolved Instructions​

Fetch the resolved instructions for the current user based on the hierarchy (Individual > Group > Team):

import { useResolvedInstructions } from '@shared/custom-instructions'

function ResolvedInstructions() {
const { data } = useResolvedInstructions()

const instructions = JSON.parse(data?.resolved_instructions || '{}')

return (
<div>
{instructions.custom_instructions?.individual_preferences && (
<p>{instructions.custom_instructions.individual_preferences.content}</p>
)}
</div>
)
}

Create Custom Instruction​

import { useCreateCustomInstruction } from '@shared/custom-instructions'
import { successToast, errorToast } from '@rhapsody/toasts'

function CreateInstructionForm() {
const { mutate, isLoading } = useCreateCustomInstruction()

const handleCreateTeamInstruction = () => {
mutate(
{
agent_name: 'sales_strategist',
instructions: 'Team-wide guidance text',
instruction_type: 'team'
},
{
onSuccess: () => successToast({ title: 'Instruction created!' }),
onError: () => errorToast({ title: 'Failed to create instruction' })
}
)
}

const handleCreateGroupInstruction = () => {
mutate({
agent_name: 'sales_strategist',
instructions: 'Group-specific guidance',
instruction_type: 'group',
group_ids: [3312, 430]
})
}

const handleCreateIndividualInstruction = () => {
mutate({
agent_name: 'sales_strategist',
instructions: 'Individual user guidance',
instruction_type: 'individual',
target_user_id: 9421
})
}

return (
<div>
<button onClick={handleCreateTeamInstruction}>Create Team</button>
<button onClick={handleCreateGroupInstruction}>Create Group</button>
<button onClick={handleCreateIndividualInstruction}>Create Individual</button>
</div>
)
}

Update Custom Instruction​

import { useUpdateCustomInstruction } from '@shared/custom-instructions'

function UpdateInstructionForm({ id }: { id: number }) {
const { mutate } = useUpdateCustomInstruction()

const handleUpdate = () => {
mutate({
id,
params: {
instructions: 'Updated instruction text',
agent_name: 'sales_strategist_manager'
}
})
}

// Transform instruction type
const handleTransformToGroup = () => {
mutate({
id,
params: {
instruction_type: 'group',
group_ids: [3312, 430, 3319]
}
})
}

return (
<div>
<button onClick={handleUpdate}>Update Text</button>
<button onClick={handleTransformToGroup}>Transform to Group</button>
</div>
)
}

Delete Custom Instruction​

import { useDeleteCustomInstruction } from '@shared/custom-instructions'

function DeleteInstructionButton({ id }: { id: number }) {
const { mutate, isLoading } = useDeleteCustomInstruction()

const handleDelete = () => {
if (confirm('Are you sure?')) {
mutate(id)
}
}

return (
<button onClick={handleDelete} disabled={isLoading}>
Delete
</button>
)
}

Bulk Delete Custom Instructions​

import { useBulkDeleteCustomInstructions } from '@shared/custom-instructions'

function BulkDeleteInstructions({ ids }: { ids: number[] }) {
const { mutate, isLoading } = useBulkDeleteCustomInstructions()

const handleBulkDelete = () => {
if (confirm(`Delete ${ids.length} instructions?`)) {
mutate({ custom_instruction_ids: ids })
}
}

return (
<button onClick={handleBulkDelete} disabled={isLoading}>
Delete Selected ({ids.length})
</button>
)
}

Features​

  • ✅ Automatic Cache Invalidation: All mutation hooks automatically invalidate relevant queries after successful operations
  • ✅ TypeScript Support: Fully typed with TypeScript for enhanced developer experience
  • ✅ Error Handling: Built-in error handling with throwIfNotOkHook for consistent error responses
  • ✅ Pagination & Sorting: Support for pagination and sorting in list queries
  • ✅ Type Transformations: Update instructions can transform between types (team ↔ group ↔ individual)

API Reference​

Query Hooks​

  • useCustomInstructions(params?, options?) - Fetch paginated list of custom instructions
  • useAllCustomInstructions(params?, options?) - Fetch ALL custom instructions across all pages automatically
  • useCustomInstruction(id, options?) - Fetch a single custom instruction
  • useResolvedInstructions(params, options?) - Fetch resolved instructions for current user

Mutation Hooks​

  • useCreateCustomInstruction() - Create a new custom instruction
  • useUpdateCustomInstruction() - Update an existing custom instruction
  • useDeleteCustomInstruction() - Delete a custom instruction
  • useBulkDeleteCustomInstructions() - Delete multiple custom instructions

API Functions​

  • getCustomInstructions(params?) - Fetch list of instructions (non-hook)
  • getCustomInstruction(id) - Fetch single instruction (non-hook)
  • getResolvedInstructions(params) - Fetch resolved instructions (non-hook)
  • createCustomInstruction(params) - Create instruction (non-hook)
  • updateCustomInstruction(id, params) - Update instruction (non-hook)
  • deleteCustomInstruction(id) - Delete instruction (non-hook)
  • bulkDeleteCustomInstructions(params) - Bulk delete instructions (non-hook)

Utility Functions​

  • invalidateCustomInstructionsQueries() - Invalidate all custom instructions queries
  • invalidateCustomInstructionQuery(id) - Invalidate a specific instruction query
  • invalidateResolvedInstructionsQueries() - Invalidate resolved instructions queries

Types​

type InstructionType = 'team' | 'group' | 'individual'
type AgentName = 'sales_strategist' | 'sales_strategist_manager'

interface CustomInstruction {
id: number
agent_name: AgentName
instructions: string | null
instruction_type: InstructionType
user_id: number | null
group_id: number | null
group_ids: number[] | null
collection_id: string | null
created_by_user_id: number
team_id: number
created_at: string
updated_at: string
}

interface CreateCustomInstructionParams {
agent_name: AgentName
instructions: string
instruction_type: InstructionType
target_user_id?: number // Required for 'individual' type
group_ids?: number[] // Required for 'group' type
}

interface UpdateCustomInstructionParams {
agent_name?: AgentName
instructions?: string
instruction_type?: InstructionType
target_user_id?: number
group_ids?: number[]
}

interface ListCustomInstructionsParams {
per_page?: number
page?: number
sort_by?: 'created_at' | 'instructions'
sort_direction?: 'asc' | 'desc'
}

Authorization & Feature Flags​

Important: All endpoints require:

  • Team Admin privileges
  • The :coaching_agent_insights feature flag must be enabled

Non-admin users or teams without the feature flag will receive a 403 Forbidden error.

Validation Rules​

Character Limits​

  • Instructions text: Maximum 2000 characters

Type-Specific Requirements​

  • Individual: Must provide target_user_id
  • Group: Must provide group_ids (non-empty array)
  • Team: No additional parameters required

Allowed Values2000 characters​

Type-Specific Requirements​

TypeRequired Fields
teamNone
groupgroup_ids (non-empty array)
individualtarget_user_id

Allowed Values​

  • agent_name: sales_strategist | sales_strategist_manager
  • instruction_type: team | group | individual

Support​

For issues or questions, contact:

  • Team: @SalesLoft/analytics-aces
  • Additional Approvers: @SalesLoft/metric-mavericks