Skip to main content

Clients

Create a client in a Refrens business, then get it using clientId — your own customer reference, which you choose.

Authentication

Like every Refrens API endpoint, the Clients API is authorised with an app token sent as Authorization: Bearer <jwt>. You can obtain the token in either of two ways:

  • Call Create Token with strategy: app-secret, or
  • Self-sign a token with your private key (strategy: app-iss-app-token).

The calling app must be attached to the business in the path, otherwise the request is rejected with 403 PERMISSION_DENIED. A user session token is not accepted on these endpoints and is rejected with 401.

What this API does not do yet

Only create and get one are available — anything else returns 405, see Unsupported operations. Until editing ships, corrections are made in Refrens.

Quick start

  1. Create a client:

    POST /api/v1/businesses/:urlKey/clients
    { "clientId": "acme-042", "name": "Acme Traders Pvt Ltd" }
  2. Get it back:

    GET /api/v1/businesses/:urlKey/clients/acme-042

clientId and name are the only required fields. Both requests need Authorization: Bearer <jwt>, and the create also needs Content-Type: application/json.

Create New Client

post /api/v1/businesses/:urlKey/clients

Creates a client in the given business with full billing identity, address, tax ids, shipping addresses, bank accounts and custom fields in one call. clientId is your own reference for the customer and makes the call safe to retry.

When omitted, the API derives panNumber and gstState from gstin, and fills taxPayerType, clientType and locale. A value you send is always kept — deriving never overwrites it.

Path Params

NameDescription
urlKeyThe business urlKey provided by Refrens.

Headers

NameTypeValue Description
Content-Type *stringapplication/json
Authorization *stringBearer <jwt>

Body — identity and contact

NameTypeValue Description
clientId *stringYour customer reference. Unique within the business, maximum 128 characters, trimmed before storing. See Idempotency for retry behaviour.
name *stringClient name. At least 1 character after trimming, maximum 500.
aliasstringShort name / alias. Maximum 500 characters.
clientTypestringINDIVIDUAL or COMPANY. Derived from the client's tax ids when omitted.
isClientbooleanWhether this party is a customer. Defaults to true.
isVendorbooleanAlso marks the party a vendor. Defaults to false. isClient and isVendor cannot both be false.
emailstringValidated as an email address.
phonestringNormalised to + followed by digits — brackets, spaces and hyphens are stripped and a leading + added if absent, so (91) 12345-67890 is stored as +911234567890. Maximum 30 characters.
phoneShowInInvoicebooleanShow the phone number on invoices. Defaults to false.
emailShowInInvoicebooleanShow the email address on invoices. Defaults to false.
dueInDaysnumberDefault payment terms for this client, in days.
localestringLocale for the client's documents, e.g. en-IN. Derived from the client's country — or the business's country when the client has none — when omitted.

Body — address

NameTypeValue Description
streetstringMaximum 2,500 characters.
buildingstringMaximum 500 characters.
citystringMaximum 500 characters.
districtstringMaximum 500 characters.
statestringMaximum 500 characters. Requires country in the same request.
pincodestringMaximum 20 characters.
countrystringISO 3166-1 alpha-2 country code. Defaults to the business's own country when omitted.

Body — tax identifiers

NameTypeValue Description
gstinstring15-character Indian GSTIN, including the UIN, TDS/TCS and OIDAR forms. Fills panNumber and gstState when those are omitted.
gstStatestringIndian GST state code, e.g. 29. Derived from gstin when omitted.
panNumberstring10 alphanumeric characters. Derived from gstin when omitted — a GSTIN that carries no PAN simply leaves it empty, which is not an error.
trnNumberstringUAE TRN — 15 digits beginning with 1.
tinNumberstringMalaysia TIN — 10 to 15 uppercase letters or digits.
sstNumberstringMalaysia SST — AAA-9999-99999999 or A99-9999-99999999.
taxPayerTypestringFrom the vocabulary for the business's country (see below). The API rejects this field for businesses outside India and the UAE. Derived when omitted.
vatNumberstringStored as given; not format-validated. Maximum 500 characters.
vatLabelstringLabel shown against the VAT number. Defaults to VAT Number.

taxPayerType accepted values, by the business's country:

Business countryAccepted values
India (IN)B2C REG UNREG EXP SEZ TDS DEX COM
UAE (AE)VATR VATNR GCCVATR GCCVATNR NGCC VATRDZ VATRNDZ

Body — lists

NameTypeValue Description
additionalIdsarray[object]Country-specific identifiers. Entry shape and vocabulary under Additional ids.
shippingDetailsarray[object]Shipping addresses. Entry shape under Shipping addresses.
bankAccountsarray[object]The client's bank accounts. Entry shape under Bank accounts.
customFieldsobjectCustom field values keyed by the field's display name, e.g. { "Industry": "SaaS" }. Rules and errors under Custom fields.

Any field not in the tables above is rejected with 400 INVALID_FIELD, naming the key.

Full sample body

{
"clientId": "acme-042",
"name": "Acme Traders Pvt Ltd",
"alias": "Acme",
"email": "billing@acme.example",
"phone": "(91) 12345-67890",
"street": "1 MG Road",
"building": "Tower A",
"city": "Bengaluru",
"district": "Bengaluru Urban",
"state": "Karnataka",
"pincode": "560001",
"country": "IN",
"gstin": "29AAPFU0939F1ZV",
"dueInDays": 15,
"additionalIds": [
{
"label": "UDY",
"value": "UDYAM-KA-03-1234567",
"showInInvoice": true
}
],
"shippingDetails": [
{
"name": "Warehouse",
"street": "Dock Road",
"city": "Bengaluru",
"country": "IN",
"gstin": "29AAPFU0939F1ZV",
"customHeaders": [{ "label": "PO", "value": "PO-1" }]
}
],
"bankAccounts": [
{
"bankAccountName": "Acme Current",
"bankAccountNumber": "000111222",
"ifscCode": "HDFC0000001",
"bankAccountType": "CURRENT",
"bankCountry": "IN",
"bankCurrency": "INR",
"isPrimary": true
}
],
"customFields": {
"Industry": "Manufacturing"
}
}

Idempotency

clientId makes the create safe to retry — if the connection drops, send the identical request again:

CaseResponse
New clientId201 + the full client.
Same clientId, same details201 + the full client with "idempotent": true.
Same clientId, different details409 IDEMPOTENCY_CONFLICT. Nothing is changed.
Same clientId, but the body fails validation400, never 409 — whether or not the reference is taken.
Same clientId, held by a client archived in Refrens409 IDEMPOTENCY_CONFLICT. The reference stays taken.

What counts as different details. Refrens compares the client your request would store — after normalisation and after filling in derived values — against the client already stored. Details are different when they would store a different value. So these are all identical retries, not conflicts:

  • Omitting a field the API filled in for you. Send a gstin and no panNumber twice: the same PAN is derived both times.
  • Restating a derived value unchanged, or omitting a field whose stored value is its default.
  • A differently formatted but equivalent value — Billing@Acme.example and billing@acme.example, or (91) 12345-67890 and +911234567890.
  • Reordering entries in additionalIds, shippingDetails, or bankAccounts.

balance, source, createdAt and updatedAt are never part of the comparison.

Dropping a value is a change, so a retry that omits a plain optional field the first call stored — alias, say — conflicts. Send the same body you sent the first time.

No silent merging. A matching email, phone or tax id never merges two clients — identity is clientId alone. A client created through this API and one created as a side effect of an invoice for the same customer stay two separate records.

Additional ids

Each entry is { label, value, showInInvoice, useForEInvoice }, where label is the ID code below, not its display name. value is required. One entry per code — the same code twice is rejected.

Available codes depend on the client's country and role: client codes need isClient, seller codes need isVendor, and both suits either.

CountryCodeIdentifierScopeFormat
India (IN)UDYUdyam Registration NumberbothUDYAM-XX-00-0000000
IECImporter Exporter Codeseller10 uppercase letters or digits
LUTLetter of Undertakingseller15 characters — two letters, twelve digits, then one letter or digit
Saudi Arabia (SA)NATNational IDclientany non-empty value
TINTax Identification Numberclientany non-empty value
IQAIqama Numberclientany non-empty value
PASPassport IDclientany non-empty value
GCCGCC IDclientany non-empty value
CRNCommercial Registration Numberbothany non-empty value
MOMMomra Licensebothany non-empty value
MLSMLSD Licensebothany non-empty value
SAGSagia Licensebothany non-empty value
OTHOther IDbothany non-empty value
HQGroup VAT Numbersellerany non-empty value
Malaysia (MY)BRNBusiness Registration Numberbothany non-empty value
NRICNational Registration Identity Cardbothany non-empty value
PASSPORTPassport Numberbothany non-empty value

A code outside the list for the client's country — or any code at all when the client has no country set — is rejected with 400 INVALID_FIELD_VALUE.

useForEInvoice selects the identifier e-invoicing reads in Saudi Arabia and Malaysia. At most one entry per client can carry it: setting it on several is not refused, but only the first entry in the array keeps the flag. India's e-invoicing never reads it — sending it on an Indian client is accepted and has no effect.

Shipping addresses

Each shippingDetails[] entry accepts name, street, building, city, district, state, pincode, country, gstin, gstState, sstNumber, tinNumber, taxId, taxPayerType, additionalIds[] and customHeaders[] of { label, value }.

FieldsRule
name, building, city, district, stateMaximum 500 characters.
streetMaximum 2,500 characters.
pincodeMaximum 20 characters.
country, gstin, gstState, sstNumber, tinNumberValidated exactly as the client's own fields of the same name.
taxId, taxPayerTypeFree text, maximum 500 characters. No vocabulary check.
additionalIds[]Same codes and formats as the client's own additionalIds.
customHeaders[]label maximum 500 characters, value maximum 2,500.

An address's additionalIds are validated against the client's own country and party type, not the address's. Nothing is derived inside a shipping address — a shipping gstin does not fill that address's gstState or panNumber.

Bank accounts

Each bankAccounts[] entry accepts:

NameTypeValue Description
bankAccountNamestringMaximum 500 characters.
bankAccountHolderNamestringMaximum 500 characters.
bankAccountNumberstringMaximum 2,500 characters.
ifscCodestringMaximum 500 characters.
ibanstringMaximum 500 characters.
sortCodestringNN-NN-NN or NNNNNN. Only valid when bankCountry is GB or IE.
swiftstringMaximum 500 characters.
bankAccountTypestringSAVINGS or CURRENT.
bankCountrystringISO 3166-1 alpha-2 country code.
bankCurrencystringISO 4217 currency code.
isPrimarybooleanMarks the client's primary account. Defaults to false.

No operation in this API removes a bank account — that is done in Refrens.

Custom fields

customFields is an object keyed by the display name the business sees in Refrens, e.g. { "Industry": "SaaS" }. Internal field keys never appear in either direction.

  • Custom fields must be enabled for the business, otherwise 400 CUSTOM_FIELDS_NOT_ENABLED.
  • Every name must match a live, non-archived Clients custom field. Unknown names are reported together in one 400 INVALID_CUSTOM_FIELD.
  • A name that resolves but carries a value the field does not accept is 400 INVALID_CUSTOM_FIELD_VALUE.
  • The API never creates a custom field or an option value.

Get a Client

get /api/v1/businesses/:urlKey/clients/:clientId

Returns the client stored under clientId. Use this endpoint after creating a client to confirm what the API stored. See Response shape for the returned fields.

Path Params

NameDescription
urlKeyThe business urlKey provided by Refrens.
clientIdThe reference you sent as clientId when the client was created.

Headers

NameTypeValue Description
Authorization *stringBearer <jwt>

No request body and no query parameters — any query parameter is rejected with 400 INVALID_QUERY_FIELD.

Visibility

GET returns a client only when it exists in the business your app is attached to and has not been archived or deleted in Refrens. Everything else returns one indistinguishable 404 CLIENT_NOT_FOUND.

This endpoint addresses clients by clientId, so it reaches only clients created through this API. Clients created elsewhere in Refrens carry no clientId and cannot be fetched here. Until search ships, create the clients you need to read.

Vendors created through this API are readable here too.


Response shape

Both operations return the client in the same shape:

  • Your own reference as clientId.
  • Every stored field from the create tables above, including the three lists.
  • customFields, keyed by display name.
  • sourceAPI for a client created through this API. Clients created elsewhere in Refrens carry their own origin, such as INVOICE, CLIENT_DASHBOARD, CLIENT_UPLOAD, DASHBOARD, INVOICE_BULK_UPLOAD, LEADFORM, PORTFOLIO, ZOHO, MARIKA or LEADAPI.
  • createdAt and updatedAt.
  • balance — a read-only summary derived from the client's documents and payments, with invoice, proforma, creditNote, paymentReceipt, debitNote, salesOrder, invoicePayment, proformaPayment, expenditure, expenditurePayment, creditConsumed and currency. It changes as the client transacts, without this API being called.

Create adds "idempotent": true on a retry that matched. No Refrens-internal identifier appears in any response, at any depth, and no error message names an internal field.

Error codes

error.codeStatusWhen
INVALID_FIELD400A body key outside the accepted set, a server-owned field, a wrongly typed value, or a body that is not a JSON object.
INVALID_FIELD_VALUE400An accepted field carrying a malformed or unsupported value, or a missing clientId / name. Names the field as you sent it.
INVALID_QUERY_FIELD400Any query parameter on fetch, which takes none.
CUSTOM_FIELDS_NOT_ENABLED400customFields sent to a business without custom fields enabled.
INVALID_CUSTOM_FIELD400A custom field name the business has not defined, or has archived.
INVALID_CUSTOM_FIELD_VALUE400A defined custom field carrying a value it does not accept.
PERMISSION_DENIED403The calling app is not attached to this business.
CLIENT_NOT_FOUND404Unknown, archived, deleted, or another business's client.
IDEMPOTENCY_CONFLICT409Same clientId with different details, or a reference held by an archived client.
SERVICE_ERROR500Unexpected server error. The response carries a generic message.

401 responses carry no error.code — they come from authentication before this API's own error envelope applies.

Name errors versus value errors:

SurfaceBad nameBad value
Body fieldINVALID_FIELDINVALID_FIELD_VALUE
Query parameterINVALID_QUERY_FIELD— (fetch takes no parameters)
Custom fieldINVALID_CUSTOM_FIELDINVALID_CUSTOM_FIELD_VALUE

Unsupported operations

Listing, searching, editing and deleting clients are not available yet. GET on the collection, and PATCH, PUT or DELETE on a client, all return 405 — the status alone, with no error.code — so you can tell an operation that is not built apart from a client that is not there.