Skip to content

Calling the API

PULSE’s API is gRPC, package pulse.v1, plus the shared runink.ui.*.v1 services. There is no REST or JSON API for business data.

Transports

Everything is on one port.

ClientHow it connects
Native gRPC (Go, grpcurl, the Linux and Android app)HTTP/2 with content-type: application/grpc
BrowsersgRPC-Web over the same port

For browsers served from another origin, the origin must be listed in CORS_ALLOWED_ORIGINS. The allowed request headers include Authorization, X-Grpc-Web, grpc-timeout, x-agent-trace-id, and x-idempotency-key.

Authenticate

Call IdentityService/Login with a username and password. The token is in accessToken:

TOK=$(grpcurl -plaintext -d '{"username":"admin","password":"<password>"}' \
      localhost:52000 pulse.v1.IdentityService/Login | jq -r '.accessToken')

Send it on every other call:

grpcurl -plaintext -H "authorization: Bearer $TOK" -d '{}' \
      localhost:52000 pulse.v1.CampaignService/ListCampaigns

Only three things work without a token: IdentityService/Login, ModelService/GetModelStatus, and reflection (when it is turned on).

Refresh a session at POST /auth/refresh. See HTTP endpoints.

Discover the schema

With PULSE_GRPC_REFLECTION=true on a local server:

grpcurl -plaintext localhost:52000 list
grpcurl -plaintext localhost:52000 describe pulse.v1.LeadService

Reflection is off by default and must stay off on deployed instances. Without it, use the .proto files in grpc/api/proto/pulse/v1/.

Streaming calls

Every AI generator is a server-streaming RPC that returns stream StreamChunk:

FieldMeaning
textThe next piece of output. May be empty.
is_finalTrue on the last chunk
sectionA section marker, for example positioning, swot, or grounding
progress0.0 to 1.0
metadataExtra key-value data
agent_tasksActions the backend actually performed, such as a scrape. These are recorded facts, not model claims. A chunk with tasks may have no text.

Long generations can go quiet while the model thinks. The server sends heartbeat frames on streams so proxies do not drop them. Use a generous deadline: a long document can take tens of minutes on CPU.

When the model is busy and admission control is on, a call can fail with ResourceExhausted. Retry after a few minutes.

Tie runs to your screen

Set x-agent-trace-id on your calls, and send the same value as session in ActivityService/SubscribeAgentActivity. Then the activity stream shows only the runs your client started. See Agent activity.

Errors

PULSE uses standard gRPC codes and puts a sentence meant for a person in the message. Common ones:

CodeTypical cause
UnauthenticatedMissing, expired, or revoked token
PermissionDeniedLicence invalid, or your role does not allow the call
FailedPreconditionSomething must be set up first, for example a HubSpot connection or PULSE_APPLY_ENABLED
InvalidArgumentA required field is missing or has a bad value
ResourceExhaustedRate limit, or the inference plane is at capacity
UnavailableA dependency failed: HubSpot, GA4, speech synthesis, media storage
UnimplementedThe feature does not exist in this deployment, for example still-image generation

See Troubleshooting for specific messages.

A2A: calling PULSE from another agent

PULSE publishes itself as an Agent2Agent (A2A) agent for FACE and the CORE fleet:

  • Discovery: GET /.well-known/agent-card.json.
  • Calls: JSON-RPC at /a2a, with a PULSE session as a bearer token in the Authorization header. Calls without a valid session are refused.

Choose a persona with the message metadata key skill. The default is market-analyst.

SkillPersona
market-analystDiagnostics, SWOT, positioning
content-creatorChannel content
copywriterLong-form content and whitepapers
strategy-agentPrescriptive 30-day plans
radar-agentTrend and profile research