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.
| Client | How it connects |
|---|---|
| Native gRPC (Go, grpcurl, the Linux and Android app) | HTTP/2 with content-type: application/grpc |
| Browsers | gRPC-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/ListCampaignsOnly 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.LeadServiceReflection 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:
| Field | Meaning |
|---|---|
text | The next piece of output. May be empty. |
is_final | True on the last chunk |
section | A section marker, for example positioning, swot, or grounding |
progress | 0.0 to 1.0 |
metadata | Extra key-value data |
agent_tasks | Actions 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:
| Code | Typical cause |
|---|---|
Unauthenticated | Missing, expired, or revoked token |
PermissionDenied | Licence invalid, or your role does not allow the call |
FailedPrecondition | Something must be set up first, for example a HubSpot connection or PULSE_APPLY_ENABLED |
InvalidArgument | A required field is missing or has a bad value |
ResourceExhausted | Rate limit, or the inference plane is at capacity |
Unavailable | A dependency failed: HubSpot, GA4, speech synthesis, media storage |
Unimplemented | The 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 theAuthorizationheader. Calls without a valid session are refused.
Choose a persona with the message metadata key skill. The default is
market-analyst.
| Skill | Persona |
|---|---|
market-analyst | Diagnostics, SWOT, positioning |
content-creator | Channel content |
copywriter | Long-form content and whitepapers |
strategy-agent | Prescriptive 30-day plans |
radar-agent | Trend and profile research |