CLI JSON Output Reference¶
This document describes the structure of JSON output from Affinity CLI commands.
Table of Contents¶
Standard Response Format¶
All CLI commands support the --json flag for machine-readable output. The response follows a consistent structure:
{
"ok": true,
"command": "person get",
"data": {
"person": {
"id": 12345,
"firstName": "John",
"lastName": "Doe",
"emailAddresses": ["john@example.com"]
}
},
"meta": {
"durationMs": 234,
"resolved": {},
"pagination": null,
"rateLimit": {
"limit": 300,
"remaining": 299,
"reset": 1609459200
}
}
}
Top-Level Fields¶
- ok (boolean):
trueif the command succeeded,falseif it failed - command (string): The command that was executed (e.g.,
"person get") - data (object): The command's result data (structure varies by command)
- meta (object): Metadata about the command execution
Meta Object¶
The meta object contains:
- durationMs (number): How long the command took to execute (in milliseconds)
- resolved (object): Information about how CLI inputs were resolved to API parameters
- pagination (object | null): Pagination metadata for list commands
- rateLimit (object): API rate limit information
Resolved Metadata¶
The meta.resolved field contains information about how CLI inputs were resolved to API parameters. The structure varies by command type based on what inputs need resolution.
Entity Commands¶
For person, company, and opportunity commands, the resolved metadata includes information about how entity selectors were resolved:
{
"resolved": {
"person": {
"input": "john@example.com",
"personId": 12345,
"source": "email",
"canonicalUrl": "https://app.affinity.co/persons/12345"
},
"fieldSelection": {
"fieldIds": ["field-123", "field-456"],
"fieldTypes": ["global"]
},
"expand": ["lists", "interactions"]
}
}
Entity Selector Resolution¶
The entity object (e.g., person, company, opportunity) contains:
- input (string): The original selector you provided
- {entityType}Id (number): The resolved entity ID
- source (string): How the selector was resolved
"id": Direct numeric ID"url": Affinity URL"email": Email address (persons only)"name": Name search (persons only)"domain": Domain search (companies only)- canonicalUrl (string, optional): The canonical Affinity URL for the entity
Field Selection¶
When field selection options are used (e.g., --field-id, --field-type), the fieldSelection object contains:
- fieldIds (array of strings): Specific field IDs requested
- fieldTypes (array of strings): Field types requested (e.g.,
"global","list-specific")
Expansion¶
When --expand is used, the expand array contains the expansions requested (e.g., ["lists", "interactions"]).
List Commands¶
For list-related commands, the resolved metadata includes list and saved view resolution:
{
"resolved": {
"list": {
"input": "Sales Pipeline",
"listId": 789,
"source": "name"
},
"savedView": {
"input": "My Active Deals",
"savedViewId": 456,
"name": "My Active Deals"
}
}
}
List Resolution¶
The list object contains:
- input (string): The original list selector
- listId (number): The resolved list ID
- source (string): How the list was resolved (
"id","url", or"name")
Saved View Resolution¶
When a saved view is specified, the savedView object contains:
- input (string): The original saved view selector
- savedViewId (number): The resolved saved view ID
- name (string): The name of the saved view
Opportunity Commands¶
Opportunity commands may include additional resolution metadata:
{
"resolved": {
"opportunity": {
"input": "OPP-123",
"opportunityId": 12345,
"source": "id"
},
"list": {
"input": "789",
"listId": 789,
"source": "id"
}
}
}
Pagination Metadata¶
For commands that return collections, pagination metadata is namespaced by collection type:
{
"data": {
"persons": [
{"id": 1, "firstName": "Alice"},
{"id": 2, "firstName": "Bob"}
]
},
"meta": {
"pagination": {
"persons": {
"nextCursor": "eyJpZCI6MTIzfQ==",
"prevCursor": null
}
}
}
}
Pagination Object Structure¶
The pagination key always matches the data key (persons, companies, opportunities, rows, etc.). Each pagination object contains:
- nextCursor (string | null): Cursor for the next page, or
nullif this is the last page - prevCursor (string | null): Cursor for the previous page, or
nullif this is the first page
Using Pagination Cursors¶
To fetch the next page, use the --cursor option:
xaffinity person search "Alice" --json
# Get nextCursor from response
xaffinity person search "Alice" --cursor "eyJpZCI6MTIzfQ==" --json
Rate Limit Metadata¶
All API responses include rate limit information:
{
"meta": {
"rateLimit": {
"limit": 300,
"remaining": 299,
"reset": 1609459200
}
}
}
Rate Limit Fields¶
- limit (number): Total number of requests allowed per time window
- remaining (number): Number of requests remaining in the current window
- reset (number): Unix timestamp when the rate limit window resets
Error Responses¶
When a command fails, the response structure changes:
{
"ok": false,
"command": "person get",
"error": {
"type": "api_error",
"message": "Person not found",
"statusCode": 404,
"details": {
"personId": 99999
}
},
"meta": {
"durationMs": 123
}
}
Error Object¶
The error object contains:
- type (string): Error category
"api_error": API returned an error"usage_error": Invalid command usage"validation_error": Input validation failed"network_error": Network/connection issue- message (string): Human-readable error description
- statusCode (number, optional): HTTP status code for API errors
- details (object, optional): Additional error context
Examples¶
Person Get by Email¶
Command:
xaffinity person get email:john@example.com --json
Response:
{
"ok": true,
"command": "person get",
"data": {
"person": {
"id": 12345,
"firstName": "John",
"lastName": "Doe",
"emailAddresses": ["john@example.com"]
}
},
"meta": {
"durationMs": 156,
"resolved": {
"person": {
"input": "email:john@example.com",
"personId": 12345,
"source": "email",
"canonicalUrl": "https://app.affinity.co/persons/12345"
}
},
"pagination": null,
"rateLimit": {
"limit": 300,
"remaining": 298,
"reset": 1609459200
}
}
}
Person Search with Pagination¶
Command:
xaffinity person search "Alice" --json
Response:
{
"ok": true,
"command": "person search",
"data": {
"persons": [
{
"id": 1,
"firstName": "Alice",
"lastName": "Smith",
"emailAddresses": ["alice@example.com"]
},
{
"id": 2,
"firstName": "Alice",
"lastName": "Jones",
"emailAddresses": ["ajones@example.com"]
}
]
},
"meta": {
"durationMs": 234,
"resolved": {},
"pagination": {
"persons": {
"nextCursor": "eyJpZCI6Mn0=",
"prevCursor": null
}
},
"rateLimit": {
"limit": 300,
"remaining": 297,
"reset": 1609459200
}
}
}
List Rows with Saved View¶
Command:
xaffinity list rows "Sales Pipeline" --saved-view "Active Deals" --json
Response:
{
"ok": true,
"command": "list rows",
"data": {
"rows": [
{
"id": 101,
"listId": 789,
"entityId": 12345
}
]
},
"meta": {
"durationMs": 189,
"resolved": {
"list": {
"input": "Sales Pipeline",
"listId": 789,
"source": "name"
},
"savedView": {
"input": "Active Deals",
"savedViewId": 456,
"name": "Active Deals"
}
},
"pagination": {
"rows": {
"nextCursor": null,
"prevCursor": null
}
},
"rateLimit": {
"limit": 300,
"remaining": 296,
"reset": 1609459200
}
}
}
Field Naming Conventions¶
All field names in JSON output use camelCase to match the Affinity API conventions:
firstName(notfirst_name)emailAddresses(notemail_addresses)nextCursor(notnext_cursor)
This applies to both the data section and all metadata fields.
JSON Output vs Table Output¶
Important differences between --json and table output:
- Completeness: JSON output includes all fields from the API response, while table output may filter or format fields for readability
- Filter Flags: Flags like
--list-entry-fieldand--fieldthat control table formatting are ignored in JSON mode - Consistency: JSON structure is stable and suitable for programmatic parsing
- Metadata: JSON includes full metadata (resolved, pagination, rate limits) that isn't shown in tables
When writing scripts or integrations, always use --json for reliable, complete output.
TypeScript Type Definitions¶
For TypeScript users, here are type definitions for the CLI output structure:
// Standard response wrapper
interface CLIResponse<T = unknown> {
ok: boolean;
command: string;
data?: T;
error?: CLIError;
meta: CLIMeta;
}
// Error object (when ok = false)
interface CLIError {
type: 'api_error' | 'usage_error' | 'validation_error' | 'network_error';
message: string;
statusCode?: number;
details?: Record<string, unknown>;
}
// Metadata object
interface CLIMeta {
durationMs: number;
resolved: ResolvedMetadata;
pagination: PaginationMetadata | null;
rateLimit: RateLimitInfo;
}
// Resolved metadata (structure varies by command)
interface ResolvedMetadata {
person?: EntityResolution;
company?: EntityResolution;
opportunity?: EntityResolution;
list?: ListResolution;
savedView?: SavedViewResolution;
fieldSelection?: FieldSelection;
expand?: string[];
}
interface EntityResolution {
input: string;
personId?: number;
companyId?: number;
opportunityId?: number;
source: 'id' | 'url' | 'email' | 'name' | 'domain';
canonicalUrl?: string;
}
interface ListResolution {
input: string;
listId: number;
source: 'id' | 'url' | 'name';
}
interface SavedViewResolution {
input: string;
savedViewId: number;
name: string;
}
interface FieldSelection {
fieldIds?: string[];
fieldTypes?: string[];
}
// Pagination (key matches data collection name)
type PaginationMetadata = {
[collectionName: string]: {
nextCursor: string | null;
prevCursor: string | null;
};
};
// Rate limit info
interface RateLimitInfo {
limit: number;
remaining: number;
reset: number;
}
// Example usage:
interface PersonGetResponse extends CLIResponse<{ person: Person }> {
data: {
person: Person;
};
}
interface PersonSearchResponse extends CLIResponse<{ persons: Person[] }> {
data: {
persons: Person[];
};
}
Related Documentation¶
- CLI Commands Reference - Complete command documentation
- CLI Scripting Guide - Working with JSON output and pagination
- CSV Export Guide - Exporting data to CSV files