Skip to navigation

Searching resources

How the POST /search endpoints work: the search_fields and filter_fields body, what each resource accepts, and the rules that decide whether a field is applied or quietly dropped.


Why this matters

A list endpoint takes query parameters and answers “give me the next page”. A search endpoint takes a body and answers “find the ones that look like this”. Multiple resources expose one, all on the same body shape, so once you can build the body for invoices you can build it for subscriptions, products or workflow executions.

The body has two parts:

  • search_fields is text search. It matches on names, references and other free-text attributes, with wildcards and case handling you control.
  • filter_fields is exact filtering. It matches ids, statuses, types and date or amount ranges.

Both are lists, both are optional, and a resource has to match all the entries you send. There is no OR between entries.

The request

Request
curl -X POST https://test.api.solvimon.com/v1/pricing-plan-subscriptions/search \
-H "X-API-KEY: <apiKey>" \
-H "Content-Type: application/json" \
-d '{
"search_fields": [
{ "name": "reference", "value": "ACME", "wildcard_start": false }
],
"filter_fields": [
{ "name": "customer_id", "value": "cust_UwDeeN0vxxHxMPCaeb1R" },
{ "name": "type", "value": "STANDARD" },
{ "name": "status", "values": ["ACTIVE"] }
]
}'

That reads: active standard subscriptions of one customer whose reference starts with “ACME”, ignoring case.

Response
{
"data": [
{
"object_type": "PRICING_PLAN_SUBSCRIPTION",
"id": "ppsu_8Hq2pqJnwo42rzGV7abcd",
"reference": "acme-platform-2026",
"name": "Acme platform subscription",
"customer_id": "cust_UwDeeN0vxxHxMPCaeb1R",
"type": "STANDARD",
"status": "ACTIVE"
}
],
"page": 1,
"limit": 50,
"total_number_of_pages": 1,
"links": {
"current": "https://test.api.solvimon.com/v1/pricing-plan-subscriptions/search?page=1&limit=50"
}
}

The response is the standard paginated wrapper, the same one the list endpoints return.

Text search with search_fields

FieldDefaultDescription
namenoneWhich attribute to search. Leave it out to search every text attribute the resource exposes, where that is supported.
valuenoneThe text to look for. Required.
wildcard_starttrueAllow anything before the value.
wildcard_endtrueAllow anything after the value.
ignore_casetrueMatch regardless of case.

The two wildcard flags combine into four kinds of match:

wildcard_startwildcard_endMatch
truetrueContains. The default.
falsetrueStarts with.
truefalseEnds with.
falsefalseExact.

Sending several search_fields entries narrows the result: each one has to match.

Omitting name is not supported everywhere, and some resources have no text search at all. Each endpoint’s Search fields table below lists the names it takes, with an omitted row when a nameless entry is allowed.

Exact filtering with filter_fields

FieldDescription
nameWhich attribute to filter on.
valueA single value.
valuesA list of values, for a field that accepts several.
referenceRead only by the custom_field filter, and only on the endpoints whose filter table says value plus reference. Leave it out everywhere else.

When to use value and when to use values

A field reads one of the two, and the three behaviours look identical from outside, which is why this is the part that most often silently returns the wrong rows:

  • Single-valued fields such as customer_id, id and type read value. A values list is ignored.
  • Multi-valued fields read values, and accept a single value as a one-item list. A status filter is usually one of these.
  • A few multi-valued fields read values only. Sent as value, the filter is dropped and the request returns everything, with no error to tell you.

The Reads column in each endpoint’s filter table says which of the three a field is. Check it rather than assuming: a dropped filter looks exactly like a filter that matched a lot.

A name the endpoint does not know is not always an error. Most endpoints validate the names in both lists and answer 422 naming the entry; a few ignore the entry and return the unfiltered result, which looks like a working request that returns too much. Each subsection below says which of the two its endpoint does, so check your field names there rather than against the response.

Enum values

Where a filter is backed by an enum, the endpoint’s table below lists every value it accepts. Whether a value outside that list is refused depends on the endpoint: most validate it and answer 422 naming the field, while some match it as a plain string, so an unknown value returns no rows instead of an error. Each subsection says which.

Send enum values in upper case. The comparison is case-sensitive unless the subsection says the endpoint upper-cases for you, so active is not ACTIVE: a 422 on a validated field, and silently empty on one that is not validated.

Ranges

A range field takes its bounds as a single string in value, in the same bracket notation the list endpoints use:

NotationMeaning
[2026-01-01T00:00:00Z,2026-04-01T00:00:00Z]Between the two, both included.
{2026-01-01T00:00:00Z,2026-04-01T00:00:00Z}Between the two, both excluded.
[2026-01-01T00:00:00Z,*]From that date onwards.
[*,2026-04-01T00:00:00Z]Up to and including that date.
2026-01-01T00:00:00ZExactly that value.

A square bracket includes the bound and a curly bracket excludes it, so the two can be mixed: [100,*} is “100 or more”. Omitting both brackets is an exact match, and one bracket without the other is rejected. Dates are ISO 8601; amounts are plain numbers.

Custom fields

A custom_field filter matches a custom field by reference and value. It is written in one of two forms, and each endpoint’s filter table says which form it takes.

One form puts the reference in the reference field, which is the only place reference is read:

Customers whose "segment" custom field is "enterprise"
{
"filter_fields": [
{ "name": "custom_field", "reference": "segment", "value": "enterprise" }
]
}

The other carries both halves in value, separated by a single colon:

Products whose "line" custom field is "hardware"
{
"filter_fields": [
{ "name": "custom_field", "value": "line:hardware" }
]
}

A value with no colon, or with more than one, is rejected with custom fields needs to be formatted as key:value. A reference that matches no custom field on that resource type is rejected with Not able to find custom field with reference ….

To search custom field content as text rather than match it exactly, use the custom_field.value entry in search_fields instead. That entry carries no reference in either form, so it searches across every custom field the resource has.

Paging and ordering

Paging and ordering stay in the query string, not in the body:

Second page, newest first
curl -X POST "https://test.api.solvimon.com/v1/invoices/search?page=2&limit=25&order_by=invoice_date&order_direction=desc" \
-H "X-API-KEY: <apiKey>" \
-H "Content-Type: application/json" \
-d '{ "filter_fields": [{ "name": "status", "values": ["FINAL"] }] }'

page defaults to 1 and limit to 50. The fields you can order by are listed under Query parameters. A few search endpoints return a fixed order of their own and ignore both parameters; the subsection below says so where that is the case.


Supported fields per endpoint

Each resource below lists its search_fields names and its filter_fields names, with every value an enum filter accepts.

Customers

POST /v1/customers/search

Search fields

nameSearches
omittedAll four attributes below at once.
nameThe customer’s name.
referenceYour own reference.
emailThe customer’s email address.
custom_field.valueThe values of all custom fields. A search_fields entry has no reference, so this cannot be narrowed to one field.

Filter fields

nameReadsValues
idvalueA customer id.
statusvalue or valuesDRAFT, ACTIVE, DEPRECATED, ARCHIVED, CLOSED
typevalueORGANIZATION, INDIVIDUAL
rolevalue or valuesDEFAULT, NON_PROCESSING, SELF_BILLING, SELLER, PROCESSING_ONLY
parent_customer_idvalueA customer id.
processing_onlyvaluetrue, false
custom_fieldvalue plus referenceAny text.

An unknown or repeated name in either list is 422. role and processing_only are validated; status and type are not, so an unknown value returns no rows.

Pricing plans

POST /v1/pricing-plans/search

Search fields

nameSearches
omittedAll three attributes below at once.
nameThe plan’s name.
referenceYour own reference.
custom_field.valueThe values of all custom fields. A search_fields entry has no reference, so this cannot be narrowed to one field.

Filter fields

nameReadsValues
idvalueA pricing plan id.
typevalueSTANDARD, CUSTOM
statusvalue or valuesDRAFT, ACTIVE, DEPRECATED, ARCHIVED
variantvalue or valuesDEFAULT, RATE_CARD
customer_idvalueA customer id, which has to exist.
custom_fieldvalue as reference:valueAny text.

An unknown or repeated name in either list is 422, as is an unknown type or a customer_id that does not resolve. status and variant are not validated, so an unknown value there returns no rows. When the body also carries selection_rule_fields, a type filter has to be STANDARD.

Pricing plan subscriptions

POST /v1/pricing-plan-subscriptions/search

Search fields

nameSearches
omittedBoth attributes below at once, or an exact id match. See below.
nameThe subscription’s name.
referenceYour own reference.

Filter fields

nameReadsValues
idvalueA subscription id.
customer_idvalueA customer id.
typevalueQUOTE, BILLING, TEMPLATE
statusvalues onlyDRAFT, ACTIVE, VOID, ARCHIVED, CANCELLED

An unknown or repeated name in either list is 422. Values are not validated, so an unknown status or type returns no rows. An empty values list is dropped rather than matching nothing.

status is the one filter on the platform that reads values and nothing else. Sent as value it is dropped silently, and the response carries every status rather than the one you asked for.

Searching by id in a search box. A search_fields entry with no name whose value is a valid resource id is treated as an exact filter rather than as text: a cust_… id filters on customer_id, and a ppsu_… id filters on the subscription’s own id. This exists so a single search box can take either a name or a pasted id. An id of any other type matches nothing. No other endpoint does this.

Products

POST /v1/products/search

Search fields

nameSearches
nameThe product’s name.
referenceYour own reference.

Filter fields

nameReadsValues
statusvalue or valuesDRAFT, ACTIVE, DEPRECATED, ARCHIVED
product_categoryvalueA product category reference, which has to exist.
customer_idvalueA customer id, which has to exist.
custom_fieldvalue as reference:valueAny text.

name is required on every search_fields entry. An unknown name in either list is 422, as is an unknown status, an unknown category reference or a customer_id that does not resolve.

Product items

POST /v1/product-items/search

Search fields

nameSearches
nameThe item’s name.
referenceYour own reference.
custom_field.valueThe values of all custom fields. A search_fields entry has no reference, so this cannot be narrowed to one field.

Filter fields

nameReadsValues
product_idvalueA product id.
statusvalue or valuesDRAFT, ACTIVE, DEPRECATED, ARCHIVED
typevalueREVENUE, DEDUCTION
model_typevalueUSAGE_BASED, USAGE_BASED_CONVERSION, RECURRING, ONE_OFF, PER_SEAT, CREDITS
customer_idvalueA customer id, which has to exist.
custom_fieldvalue as reference:valueAny text.

name is required on every search_fields entry. An unknown or repeated name is 422, and so is an unknown type, model_type or status. These three are upper-cased before they are checked, so case does not matter here.

Product categories

POST /v1/product-categories/search

Search fields

nameSearches
nameThe category’s name.
referenceYour own reference.

Filter fields

nameReadsValues
customer_idvalueA customer id.

name is required on every search_fields entry, and an unknown name in either list is 422.

Invoices

POST /v1/invoices/search

Search fields

nameSearches
omittedEvery text attribute of the invoice.
invoice_numberThe invoice number.

Filter fields

nameReadsValues
idvalueAn invoice id.
typevalueSTANDARD, CREDIT, ONE_OFF, ONE_OFF_CREDIT
statusvalue or valuesFUTURE, OPEN, DRAFT, FINAL, EMPTY, VOID, ARCHIVED
billing_typevalueDEFAULT, SELF_BILLING
e_invoice_statusvalue or valuesSUBMITTED, ACCEPTED, ERROR, IGNORE, SUCCESS
linked_invoice.link_typevalueCREDIT, ORIGINAL, REPLACED, REPLACES
paidvaluetrue, false
on_holdvaluetrue, false
has_e_invoicevaluetrue, false
customer_idvalueA customer id.
customer.referencevalueA customer reference.
pricing_plan_subscription_idvalueA subscription id.
product_idvalueA product id.
product_item_idvalueA product item id.
billing_entity_idvalueA billing entity id.
invoice_amount_including_tax.currencyvalueAn ISO 4217 currency code.
invoice_amount_including_tax.quantityvalue as a numeric rangeSee Ranges.
created_at, updated_at, invoice_date, billing_period_date, delivery_date, updated_to_final_at, due_datevalue as a date rangeSee Ranges.

Every enum above is validated: an unknown value is 422 naming the field. An unknown name, though, is ignored rather than refused, so a typo in a field name quietly widens the result. customer.reference resolves to a customer and conflicts with a customer_id naming a different one. Pair invoice_amount_including_tax.quantity with invoice_amount_including_tax.currency to find every invoice over a threshold in one currency.

Reports

POST /v1/reports/search

Search fields

Not supported. A search_fields entry with a name is 422.

Filter fields

nameReadsValues
report_codevalueINVOICE, INVOICE_USAGE, INVOICE_HEADER, INVOICE_LINE, INVOICE_TAX, INVOICE_COMMITMENT, E_INVOICING, CHILD_PARENT_BREAKDOWN, CHILD_PARENT_BREAKDOWN_PLATFORM, NOT_MATCHED_METER_DATA, DUPLICATE_METER_DATA, METERING, CUSTOMER, PRODUCT, PRICING_PLAN_SUBSCRIPTION, PAYMENT, PRICING, REVENUE_RECOGNITION, CLAUSES, REPROCESS
customer_idvalueA customer id.
invoice_idvalueAn invoice id.

Reports and report generate requests share one list of accepted names, so integration_id and status are accepted here without error but have no effect on a report search. Any other name is 422. report_code is passed through without validation, so an unknown code returns no rows. Results come back ordered by report_code descending unless you pass order_by.

Report generate requests

POST /v1/report-generate-requests/search

Search fields

Not supported. A search_fields entry with a name is 422.

Filter fields

nameReadsValues
report_codevalueThe same codes as Reports, above.
statusvalue or valuesCREATED, QUEUED, SCHEDULED, GENERATING, DONE, ERROR
customer_idvalueA customer id.
invoice_idvalueAn invoice id.
integration_idvalueAn integration id.

An unknown or repeated name is 422, as is an unknown status. status is upper-cased before it is checked, so its case does not matter.

Bulk actions

POST /v1/bulk-actions/search

Search fields

Not supported. A search_fields entry is ignored.

Filter fields

nameReadsValues
typevalueFINALIZE_INVOICES, REQUEST_USAGE_REPORTS
statusvalue or valuesCREATED, QUEUED, DETERMINE_WORK, NOTHING_TODO, PROCESSING, DONE, ERROR
created_atvalue as a date rangeSee Ranges.

An unknown name is ignored. Both enums are validated. status is upper-cased first, so its case does not matter; type is not, so it has to be sent in upper case. A type entry needs a value.

Workflows

POST /v1/workflows/search

Search fields

Not supported.

Filter fields

nameReadsValues
statusvalue or valuesACTIVE, INACTIVE, DRAFT, ARCHIVED
typevalueINVOICE, PAYMENT, PRICING_PLAN_SUBSCRIPTION, WALLET, USAGE

Both are validated, so an unknown status or type is 422. A status entry with neither value nor values is 422 as well. workflowType is accepted as an older spelling of type, and a type sent as values is only read when the list holds exactly one entry. This endpoint returns its own order and ignores order_by and order_direction.

Workflow triggers

POST /v1/workflow-triggers/search

Search fields

Not supported.

Filter fields

nameReadsValues
workflow_idvalueA workflow id.
customer_idvalueA customer id.
typevalueMANUAL, EVENT

All three read value; a values list is ignored. Any other name is 422, as is an unknown type. This endpoint returns its own order and ignores order_by and order_direction. See Workflow triggers.

Workflow action executions

POST /v1/workflow-action-executions/search

Search fields

Not supported.

Filter fields

nameReadsValues
statusvalue or valuesPENDING, SUCCEEDED, FAILED, NOT_EXECUTED
workflow_idvalueA workflow id.
workflow_action_idvalueA workflow action id.
workflow_trigger_idvalueA trigger id.
invoice_idvalueAn invoice id.
customer_idvalueA customer id.
payment_idvalueA payment id.
pricing_plan_subscription_idvalueA subscription id.
wallet_idvalueA wallet id.

Any other name is 422, as is an unknown status. The id filters take the first entry of values when value is absent. This is the endpoint to use for “what did this workflow do to this invoice”, since it filters on the action, the trigger and the status together. See Monitoring and audit.

API keys

POST /v1/api-keys/search

This one does not use the shared body, and it does not page: it looks up one API key and returns it.

FieldRequiredDescription
usernameOne of the twoThe key’s username.
resource_idOne of the twoThe key’s resource id.
user_typeNoMORTAL, TOKEN, API, PROXY, EXTERNAL_CREDENTIALS. An API key is API.
include_inactiveNotrue to include keys that are no longer active.

Sending neither username nor resource_id is rejected with Neither resource_id nor username are set, and a lookup that matches nothing answers 404.


Edge cases

  • Searching needs the same VIEW permission as reading the resource. A key that cannot read invoices cannot search them either.
  • An empty body is valid on every endpoint that takes the shared body, and returns the first page of everything, which makes POST /search with {} a usable list call.
  • Text search runs against stored values, so it will not find a resource by a figure that is calculated at read time.
  • Sending the same field name twice is rejected with field … already specified on every endpoint that validates names. Combine the values into one entry instead of repeating the entry.
  • The two lists are validated independently. A name that is valid in filter_fields is not necessarily valid in search_fields, and the error names which list it came from.