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