API REFERENCE

Beta

Start a Workflow in a Session

Starts a workflow against the data staged in this session, and returns a workflow handle for following it. This is the reason the session API exists.

The body names a product and nothing else. Starting a workflow records no snapshot — it reads what the session already holds. The workflow runs against whatever snapshot is current at the moment you start it; staging again afterwards does not reach a workflow already running.

Get the URL to submit against from your Pitchpoint Account Representative, along with the service and model values your account is entitled to use. There is no endpoint that lists them.

This call starts a real workflow and is billed. Stage once, start as many workflows as you need — but each is independently billed, not a retry of the last one. Two workflows against one session give you two independent workflows, neither a reconfirm of the other. Nothing earlier in the session flow costs anything; this is the call that does.

Sample

url="https://api.pointservices.com/riskinsight-services-ws/resources/v1/sessions/0000000000013500001/workflows"
curl -X POST "${url}" \
  -H "Authorization: Bearer your_access_token_here" \
  -H "Content-Type: application/json" \
  -H "Accept: application/json" \
  -d '{
    "product": {
      "service": "ADV",
      "model": "FirstOrder"
    },
    "correlationId": "alpha_numeric_string_to_help_identify_this_workflow"
  }'

Header Properties

Property Value Required?
Authorization Bearer your token true
Content-Type application/json true
Accept application/json false
X-PPS-UserAlias your value false
X-PPS-UserAgent your value false
X-PPS-CorrelationID your value false

Send Content-Type: application/json on every POST, including one with no body at all. In deployed environments a request that arrives without it is refused by edge protection in front of the application, with a 403 carrying an HTML body rather than any response documented here — and because the service never sees the request, nothing can explain it to you. A local or unprotected deployment may accept the same request, so do not take a working call there as evidence the header is optional.

Accept is not enforced — a request without it is answered normally with JSON — but send it anyway, so that a future change to content negotiation cannot alter what you receive.

X-PPS-CorrelationID is a support-tracing header and is not the same thing as the correlationId field in a request body. Only the body field is recorded against an order and searched by Find Workflows; setting the header globally in your HTTP client does not make your orders findable.

See the Common Headers guide for details on the optional X-PPS-* headers above.

Path Parameters

Property Description Type
sessionId (mandatory) The session holding the data to run against. string

Request Data Properties

Property Description Type
product (mandatory) The product to run, as a structured selector. See section below. object
correlationId (optional) Your own reference for this run, at most 255 characters. Not required to be unique. This is the value Find Workflows searches. string
product
Property Description Type
service (mandatory) The product family, for example ADV. string
model (mandatory) The model within that family, for example FirstOrder. string

Both parts are required, and both are free text rather than a closed list. A pair that does not resolve to a product you may use is rejected — see the 400 below.

Terms do not belong here. The data comes from the session. A body carrying terms is rejected; to change it, stage it again first and then start the workflow.

The session’s correlationId is not inherited. This workflow carries its own or none at all, and only the one supplied here is searchable.

Responses

Not every response carries the same body, so branch on the status code first, and then on what the body actually contains. Some responses carry the messages envelope described on this page; others carry a JSON problem document with an entirely different set of fields; others carry no JSON at all. A client that calls its JSON parser on every non-2xx body, or that reads messages[0].code without first checking that messages is present, will fail on the responses it is most likely to meet.

202

Accepted for processing. The workflow now exists and is readable immediately — you may follow links.self the instant you receive this.

Property Description Type
workflow (present) The workflow this call started. See section below. object
messages (present) Always an array, never null. Carries one message whose code is ACCEPTED. array
links (present) self re-reads the workflow; reconfirm is the same URL with method POST, and is always present regardless of whether a reconfirm is actually available. object
workflow
Property Description Type
workflowId (present) The handle for every later read and reconfirm, wfpop_-prefixed. Stable for the life of the workflow, including across reconfirms. string
runId (present) Identifies this one execution, wfrunpop_-prefixed. No endpoint accepts a runId — it exists to match this response to a webhook event’s data.runId. string
status (present) processing on an acknowledgment. Lowercase, one of processing, complete, failed, cancelled. string
sessionId (present) The session this was started against — your way back to the data. string
correlationId (optional) Your value for this run, echoed back. Absent when none was sent. string
product (present) The resolved product selector. object
productType (present) The product family, matching product.service. string
productName (present) The model, matching product.model. string
reconfirmAvailable (optional) false on an acknowledgment, which says nothing about whether a reconfirm will ever be available. Whether it turns true later depends on the product, not merely on time passing — see Reconfirm a Workflow. boolean

No timestamps are set on an acknowledgment. ordered, fulfilled, completed, serviceable and referenceNumber are all absent until the workflow has been recorded; the only time in this response is the message’s own timestamp. Every optional field is omitted rather than null when the stage that would set it has not happened.

Example:

{
  "workflow": {
    "workflowId": "wfpop_0000000000002080001",
    "runId": "wfrunpop_0000000000002080001",
    "sessionId": "0000000000013500001",
    "correlationId": "alpha_numeric_string_to_help_identify_this_workflow",
    "product": {
      "service": "ADV",
      "model": "FirstOrder"
    },
    "productType": "ADV",
    "productName": "FirstOrder",
    "status": "processing",
    "reconfirmAvailable": false
  },
  "messages": [
    {
      "code": "ACCEPTED",
      "text": "workflow accepted for processing",
      "timestamp": "2026-08-07T15:01:42Z"
    }
  ],
  "links": {
    "self": {
      "href": "https://api.pointservices.com/riskinsight-services-ws/resources/v1/workflows/wfpop_0000000000002080001"
    },
    "reconfirm": {
      "href": "https://api.pointservices.com/riskinsight-services-ws/resources/v1/workflows/wfpop_0000000000002080001",
      "method": "POST"
    }
  }
}

400

The workflow was refused and nothing was billed. Causes: product absent, malformed, or carrying only one of service and model; a service and model pair that does not resolve to a product you may use; a terms field, which belongs on Stage a Loan; a correlationId longer than 255 characters.

Product resolution runs before the empty-session check, so naming an unresolvable product against an empty session returns this 400 rather than the 409 below. Fix the product first, then stage.

An unresolvable product pair will not become resolvable on its own. Correct the product rather than retrying — the same request returns the same 400.

A 400 arrives in two different body shapes, decided by how far the request got before it was refused. Check for a messages array first, and fall back to the problem document when it is absent.

A value this service evaluated and rejected carries the messages envelope with the code VALIDATION_ERROR.

Property Description Type
messages (present) Always an array, never null. Carries one message whose code is VALIDATION_ERROR. array
messages[].code (present) Always VALIDATION_ERROR on this shape. string
messages[].text (present) Human-readable description of what was wrong. Never machine-parsed — the wording is not contractual. string
messages[].timestamp (present) RFC 3339 UTC with a trailing Z. Fractional seconds appear only when they are not zero, so parse this value rather than slicing it. string

Example:

{
  "messages": [
    {
      "code": "VALIDATION_ERROR",
      "text": "the requested product does not resolve",
      "timestamp": "2026-08-07T15:01:42Z"
    }
  ]
}

A body that could not be read as this request at all — a JSON array where an object was expected, a field of the wrong type, or malformed JSON — is refused before the service evaluates any value, so there is nothing to put in a messages array. It carries a JSON problem document instead, with Content-Type: application/problem+json and no messages key and no VALIDATION_ERROR code.

Example:

{
  "type": "about:blank",
  "title": "Bad Request",
  "status": 400,
  "detail": "Request body has a field that must be a value of type SessionStartRequest",
  "instance": "urn:pps:request:0000000000000000000000000000000000000000000000000000"
}

A client that reads messages[0].code on every 400 without checking that messages exists will throw on this second shape. Branch on the presence of messages, not on the status.

No workflow is started, no handle is issued, and the session’s loan is left exactly as it was.

401

No credential the platform could accept reached the service.

The body is a JSON problem document, not the messages envelope, and the response carries a WWW-Authenticate: Bearer realm="Helix" challenge. Read the machine-readable part from type; title and detail are human copy and are not contractual.

Property Description Type
type (present) An absolute URI identifying the problem. about:blank when no credential was sent at all. string
title (present) Short human-readable summary. string
status (present) Repeats the HTTP status code. number
detail (present) Human-readable explanation. Never machine-parsed. string
instance (present) An identifier for this one request. Quote it when contacting support. string

Examples:

{
  "type": "about:blank",
  "title": "Unauthorized",
  "status": 401,
  "detail": "Authentication is required. Present a bearer access token in the Authorization header.",
  "instance": "urn:pps:request:0000000000000000000000000000000000000000000000000000"
}

{
  "type": "https://pointservices.com/problems/invalid-token",
  "title": "The access token was not accepted",
  "status": 401,
  "detail": "The access token was rejected.",
  "instance": "urn:pps:request:0000000000000000000000000000000000000000000000000000"
}

The second form carries WWW-Authenticate: Bearer realm="Helix", error="invalid_token", and is what a token the service will not accept returns.

Treat a 401 and a 403 alike: both mean your credential did not work, and neither distinguishes reliably enough to build separate recovery paths on. Obtain a fresh access token and retry the request once. If it fails again, stop and surface the error rather than looping — repeated attempts with a credential the service has already refused will meet the rate throttle described below.

403

Either you are not permitted to start a workflow in this session, or your account is not entitled to the product you requested. An entitlement problem is the more common of the two: check the service and model against what your Account Representative provisioned. No workflow is started and nothing is billed.

The body does not carry the messages envelope, so there is no code to branch on. Branch on the status alone, and treat the body as opaque: do not assume it is empty, and do not assume it is JSON.

A 403 is not always a permissions problem — try a fresh token first. A credential the service will not honour can be refused with either status, so a sudden wall of 403s on calls that worked a moment ago is more often an expired token than a change to your entitlements. Obtain a new access token and retry once. If it fails again, then treat it as entitlement and talk to your Account Representative.

A 403 is also produced at the edge, before this service is reached, when a request omits Content-Type or carries a value resembling SQL injection or cross-site scripting. Those refusals carry an HTML body and this service never sees them. A value that legitimately contains such text should be encoded — Base64, for instance — rather than sent raw.

404

No session exists with this identifier, or the path you called is not served.

A 404 arrives in two different body shapes, decided by whether the path is served at all.

A path this service serves, naming something that does not exist carries a JSON problem document with Content-Type: application/problem+json. Read the machine-readable part from type.

Property Description Type
type (present) An absolute URI identifying the problem, ending /problems/not-found here. string
title (present) Short human-readable summary. string
status (present) Repeats the HTTP status code. number
detail (present) Human-readable explanation naming what was not found. Never machine-parsed. string
instance (present) An identifier for this one request. Quote it when contacting support. string

Example:

{
  "type": "https://pointservices.com/problems/not-found",
  "title": "No such resource",
  "status": 404,
  "detail": "Session[0000000000013500001] not found",
  "instance": "urn:pps:request:0000000000000000000000000000000000000000000000000000"
}

An identifier this API has already issued to you is readable the moment you receive it, so a 404 on one is final rather than a race. Retrying will not make it appear.

A path that is not served at all — most often a URL missing the riskinsight-services-ws context root — is refused by infrastructure in front of the application, which never sees the request. That response carries no body this API defines, so branch on the status alone and do not attempt to parse what comes back. Its exact shape depends on the environment you are calling.

If a 404 has no body, check the context root before anything else.

409

SESSION_EMPTY — nothing has been staged into this session, so there is nothing to run against. Stage a loan first, then try again. This is the one 409 on this API you can resolve on your own, and it is deliberately not a VALIDATION_ERROR: your request was fine, the session was empty, and the corrective action is different. Nothing is billed, because the request is refused before anything is dispatched.

WORKFLOW_WRITE_CONFLICT — a concurrent change was detected and this attempt lost the race. Final for that attempt: the service does not retry it, so you decide whether to resubmit. No code path emits this today — concurrent changes are serialized server side, and what is not yet built is the translation of a lost race into this response rather than a generic failure. Handle it anyway: the code is part of the permanent vocabulary and may begin appearing without notice.

Branch on messages[].code, never on the status alone. A 409 on this surface means more than one thing, and the corrective action differs by cause.

Property Description Type
messages (present) Always an array, never null. array
messages[].code (present) One of the codes described above for this status. string
messages[].text (present) Human copy. Never machine-parsed. string
messages[].timestamp (present) RFC 3339 UTC with a trailing Z, fractional seconds only when they are not zero. string

A conflict can also arrive without a messages array, as a JSON problem document carrying type, title, status, detail and instance. When messages is absent, read the machine-readable part from the last path segment of type. Treat the conflict as final for that attempt in either shape, and check that messages is present before reading messages[0].code.

429

Refused by a rate throttle at the edge, before this service is reached. The throttle counts requests per source address across every endpoint on both the session and workflow surfaces.

The refusal carries no body, no messages array and no Retry-After header, and this service never sees it, so it will not appear in any support trace.

Back off and resume. When following a workflow to completion, an interval that stays clear of the throttle is two seconds for the first two checks, then five, then ten seconds thereafter, with an overall deadline after which you stop and investigate.

500

An unhandled failure inside the service. The body does not carry the messages envelope, so branch on the status alone.

A 500 means the outcome is unknown, not that nothing happened. A failure raised after a write has already committed leaves that write in place. A workflow may have been started and billed. Do not simply retry. Search Find Workflows for the correlationId you sent, and start another only if nothing comes back — which is the reason to send a distinctive one every time. Retrying blind is how one order becomes two, and nothing on this surface removes a duplicate for you.


Copyright © Pitchpoint Solutions. All rights reserved.