Ria Money Transfer

Errors

API errors follow standard HTTP status codes. Learn the error response format and how to resolve the most common errors.

Error responses share one body shape: an apiClientMessages array. Every message carries a category and a machine-readable code; the remaining fields add human-readable context:

Error response
HTTP/1.1 400 Bad Request
Content-Type: application/json

{
  "apiClientMessages": [
    {
      "category": "ERROR",
      "code": "FORMAT_ERROR",
      "path": "/validTo",
      "text": "Value of validTo is not a valid future date."
    }
  ]
}
Field Description
apiClientMessages Array of message blocks — a single response can report several issues. Each block carries the fields below.
category ERROR or WARNING. Required.
code Machine-readable message code, for example FORMAT_ERROR. Required — branch your error handling on this field, not on the human-readable text.
path Path to the request attribute the message refers to, for example /validTo.
text Human-readable explanation of the message, up to 500 characters.

The exact statuses and codes each operation can return are documented per endpoint in the API reference.

Common errors

The errors you are most likely to meet while integrating:

Status Code Description
400 FORMAT_ERROR The request body or a header fails validation — a required attribute is missing, a value has the wrong type, or a date is malformed. Read detail and instance, fix the flagged attribute, and resend.
400 CONSENT_UNKNOWN The Consent-ID header references a consent that does not exist. Pass the consentId returned when the consent was created, without modification.
401 CERTIFICATE_INVALIDCERTIFICATE_EXPIRED The QWAC presented during the TLS handshake was rejected or has expired. Verify the certificate is a valid QWAC with the AISP or PISP role you are using, and renew it if expired. The sandbox also accepts test certificates.
401 SIGNATURE_INVALIDSIGNATURE_MISSING The x-jws-signature header is absent or does not verify against the request. Work through the signing failures checklist below.
401 CONSENT_INVALIDCONSENT_EXPIRED The consent is not in the valid status — the customer has not authorised it yet, revoked it, or its validTo date has passed. Check the consent status endpoint; if the consent expired or was revoked, create a new one and send the customer through authorisation again.
401 PSU_CREDENTIALS_INVALID The customer login failed during authorisation. In the sandbox, use the credentials from the Test credentials page.
403 SERVICE_BLOCKED The requested service is not enabled for this provider. Confirm the endpoint is listed in the API reference; contact support if it is.
404 RESOURCE_UNKNOWN The resource identifier or the request path is wrong. Check the identifier and the full path, including the provider code and the /api/v2 segment.
405 SERVICE_INVALID The HTTP method is not supported on this path. Check the method for the operation in the API reference.
409 STATUS_INVALID The action is not allowed in the current status of the resource — for example, deleting a payment that has already been executed. Fetch the current status first and only perform actions valid for it.

Signing failures

Most first-week integration errors are SIGNATURE_INVALID responses caused by a mismatch between the signed request and the request actually sent. Check, in order:

  • The Digest header is the SHA-256 hash of the exact body bytes you transmit — compute it after resolving any placeholders, and recompute it after every body change.
  • An empty body still needs a Digest — the hash of an empty string.
  • The headers listed in the protected header’s sigD.pars are signed with exactly the values sent — any header changed after signing invalidates the signature.
  • The signature is computed with the QSealC private key and the algorithm declared in alg.
  • The Postman collection from the Test credentials page builds both headers in its pre-request scripts — use it as a working reference for your own implementation.

How the two headers are built, end to end — the signature covers the request headers listed in sigD.pars, and the body is covered indirectly through the signed Digest:

Building the x-jws-signature (pseudocode)
# 1. Digest — hash the exact body bytes you will send
body   = resolve_placeholders(raw_body)
body   = trim(replace(body, CRLF, LF))
digest = "SHA-256=" + base64(sha256(body))   # empty body: hash of ""

# 2. Headers covered by the signature
pars = ["x-request-id", "digest"]            # always signed
for h in ["api-contract-id", "psu-id",
          "psu-corporate-id", "client-redirect-uri"]:
    if request has header h: pars.push(h)

# 3. Protected header
protected = {
  "typ":  "JOSE",
  "alg":  "RS256",
  "b64":  false,               # payload is not base64-encoded
  "crit": ["b64", "sigT", "sigD"],
  "x5c":  [base64_der(qsealc_cert)],         # PEM armour stripped
  "sigT": utc_now("YYYY-MM-DDThh:mm:ssZ"),   # no milliseconds
  "sigD": { "pars": pars,
            "mId":  "http://uri.etsi.org/19182/HttpHeaders" },
  "aud":  method + " " + path_from_v2
          # e.g. "POST /v2/consents/account-access"
}

# 4. Payload — signed headers as "name: value" lines, pars order
payload = join(pars.map(h => h + ": " + header_value(h)), "\n")

# 5. Sign and assemble (detached JWS: empty middle segment)
signing_input = base64url(json(protected)) + "." + payload
signature     = base64url(
    rsa_sha256_sign(signing_input, qsealc_private_key))

x-jws-signature = base64url(json(protected)) + ".." + signature

The two headers then look like this on the wire:

Example header values
Digest: SHA-256=hl1/Eps8BEQW58FJhDApwJXjGY4nr1ArGDHIT25vq6A=
x-jws-signature: eyJiNjQiOmZhbHNlLCJ4NXQjUzI1NiI6ImR5dFBwU2tKWXpoVGRQWFNXUDdqaFhnRzRrQ09XSVdHaWVzZHprdk5MelkiLCJjcml0IjpbInNpZ1QiLCJzaWdEIiwiYjY0Il0sInNpZ1QiOiIyMDIwLTEwLTI2VDExOjI2OjU3WiIsInNpZ0QiOnsicGFycyI6WyJ4LXJlcXVlc3QtaWQiLCJkaWdlc3QiXSwibUlkIjoiaHR0cDovL3VyaS5ldHNpLm9yZy8xOTE4Mi9IdHRwSGVhZGVycyJ9LCJhbGciOiJSUzI1NiJ9..NuGglWBtHcXavob2ZmW-PoSpMmhJS9U6z8zPqHFoE97vHI3z8wtaVdbwsj5WzgPjtVWUUnk8cxP4JjLXdwzwhhHB2uJ5FI4ZZGdSQQw-9pXPUx3cCJSzHmrgkJ-di-A4

Getting help

If a request keeps failing with a valid certificate, email compliance@saltedge.com and include the X-Request-ID of the failing call and the full error response.