Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.

Table of Contents


Conventions


The Jiwa REST API is based on the Data Transfer Object (DTO) pattern.

Every request accepts a DTO and almost every response is a DTO.  You can, in some cases encode the request DTO as URL parameters instead, such as authentication and query requests.

The DTO classes can be obtained from the following routes:

LanguageRoute
C#/types/csharp
VB.NET/types/vbnet
F#/types/fsharp
Typescript/types/typescript
Java/types/java
Kotlin/types/kotlin
Swift/types/swift

Additionally, you can use your tool of choice to generate the DTO's from the Open API specification document located at /openapi

Content Negotiation

By default the Jiwa REST API is configured to accept json DTO's and return json DTO's. This default content type can be changed in the configuration, or the client can override the content type. If using the ServiceStack client, then there is no concern of the content type as it is automatically negotiated.

In addition to using the standard Accept HTTP Header to retrieve the response a different format, you can also request an alternative Content-Type by appending ?format=ext to the query string, e.g:

    • /Debtors/{DebtorID}?format=json
    • /Debtors/{DebtorID}?format=xml
    • /Debtors/{DebtorID}?format=csv

Or by appending the format .ext to the end of the route, e.g:

    • /Debtors/{DebtorID}.json
    • /Debtors/{DebtorID}.xml
    • /Debtors/{DebtorID}.csv

Verbs

  • GET operations are used to retrieve data
  • POST operations are used to create data
  • PATCH operations are used to update existing data
  • DELETE operations are used to delete existing data

HTTP Response Codes

The response code returned will vary based on the type of operation and the result of the request.

200 OK

Will be returned for most GET operations - unless there was nothing to return, then a 204 No Content will be returned

201 Created

Will be returned for successful POST operations

204 No Content

Will be returned for successful DELETE operations

401 Not Authenticated

Will be returned if the operation requires the user to be authenticated, and they are not - or if they are authenticating and it failed due to bad credentials.

403 Forbidden

Will be returned if the user is authenticated, but do not have permission to perform the operation. See Setting Route Permissions for information on setting route permissions.

404 Not Found

Will be returned if the route is invalid, or the resource requested does not exist.

409 Conflict

Will be returned if the Jiwa business logic determines you can't perform the operation. This may occur with DELETE operations if the record cannot be deleted because it is referenced elsewhere (e.g: You can't delete a product because it is used on a sales order).

You may also get a 409 response due to a concurrency conflict.  Jiwa uses optimistic concurrency control, and if the record changes because of another users or processes actions between a read and save, then this error will be returned.

Routing

Info
titleServiceStack clients don't care about routes

Note that if using a ServiceStack client you do not need to be concerned with routes. The route is worked out by the client based on the type of the DTO being sent.

The convention used for route structure is to use a plural noun of the resource followed by the primary key (usually an ID) of the resource identifier:

No Format
/SalesOrders/{InvoiceID}

The above URL route when used as a GET operation will retrieve the sales order with the InvoiceID provided:

No Format
/SalesOrders/5244dd5e199749f4b6fe

will retrieve sales order with InvoiceID of 5244dd5e199749f4b6fe

The same applies to PATCH and DELETE operations

For POST operations, you don't know and cannot supply the InvoiceID, as it is generated for you - so it is simply a POST to:

No Format
/SalesOrders

with the content body being the DTO.

Where there is relational data, such as sales orders have many possible related notes the URL appends to the sales order route URL the plural of the relational data (Notes) and when appropriate the primary key of the note (NoteID). A GET of the following route:

No Format
/SalesOrders/5244dd5e199749f4b6fe/Notes

will retrieve all notes for InvoiceID 5244dd5e199749f4b6fe

A POST to the same route will add a new note:

No Format
/SalesOrders/5244dd5e199749f4b6fe/Notes

A PATCH to the following route updates the note with NoteID 66581f3f-3efb-4cb5-878c-37f6a55405cf which is attached to sales order with InvoiceID 5244dd5e199749f4b6fe

No Format
/SalesOrders/5244dd5e199749f4b6fe/Notes/66581f3f-3efb-4cb5-878c-37f6a55405cf

A DELETE to the same route will remove the note from the sales order

No Format
/SalesOrders/5244dd5e199749f4b6fe/Notes/66581f3f-3efb-4cb5-878c-37f6a55405cf


An alternative method of POST or PATCH operations for relational data is not to use the explicit route, but provide the relational data in a POST or PATCH of the parent route.

An example below shows the addition of a new note, the addition of a new line, and the edit of an existing line to an existing sales order using a single PATCH operation to the sales order:

Panel
borderStylesolid
titleUpdates an existing order - adds a new line, adjusts an existing line and adds a note


Deck
idSalesOrderPATCHRequest


Card
idSalesOrderPATCHRequest_csharp_servicestack
labelServiceStack Client C#


Code Block
languagec#
var client = new ServiceStack.JsonServiceClient("https://api.jiwa.com.au");
var authResponse = client.Get(new ServiceStack.Authenticate() { UserName = "admin", Password = "password" });

var salesOrderPATCHRequest = new JiwaFinancials.Jiwa.JiwaServiceModel.SalesOrderPATCHRequest()
{
	InvoiceID = "babce67cdbf64f778536",
	ExpectedDeliveryDate = DateTime.Now.AddDays(5)                             
};
salesOrderPATCHRequest.Lines.Add(new JiwaFinancials.Jiwa.JiwaServiceModel.SalesOrders.SalesOrderLine { PartNo = "1172", QuantityOrdered = 5 });
salesOrderPATCHRequest.Lines.Add(new JiwaFinancials.Jiwa.JiwaServiceModel.SalesOrders.SalesOrderLine { InvoiceLineID = "88a358a2822e4319b9b9", QuantityOrdered = 10, CustomFieldValues = new List<JiwaFinancials.Jiwa.JiwaServiceModel.CustomFields.CustomFieldValue> { new JiwaFinancials.Jiwa.JiwaServiceModel.CustomFields.CustomFieldValue { SettingID = "1ae102b94dc54dfc8a45                ", Contents = "Adjustment requested by phone" } } });

salesOrderATCHRequest.Notes.Add(new JiwaFinancials.Jiwa.JiwaServiceModel.Notes.Note { NoteText = "Customer telephoned and asked for another few hours of labour" });

var salesOrderATCHResponse = client.Patch(salesOrderATCHRequest);



Card
idSalesOrderPATCHRequest_csharp
labelC#


Code Block
languagec#
using (var webClient = new System.Net.WebClient())
{
    // Authenticate               
    webClient.QueryString.Add("username", "Admin");
    webClient.QueryString.Add("password", "password");
     
    string responsebody = webClient.DownloadString("https://api.jiwa.com.au/auth");               
    // Above returns something like this: {"SessionId":"0hKBFAnutUk8Mw6YY6DN","UserName":"api","DisplayName":"","ResponseStatus":{}}
 
    // Deserialise response into a dynamic - below requires the Newtonsoft.Json nuget package
    var authResponse = Newtonsoft.Json.JsonConvert.DeserializeObject<dynamic>(responsebody);
    var sessionId = authResponse.SessionId;
 
    webClient.Headers.Add(System.Net.HttpRequestHeader.Cookie, string.Format("ss-id={0}", sessionId));
    webClient.Headers[System.Net.HttpRequestHeader.ContentType] = "application/json";
	
	string json = Newtonsoft.Json.JsonConvert.SerializeObject(new
	{                    
		ExpectedDeliveryDate = DateTime.Now.AddDays(5),
		Lines = new List<object>
		{
			new { PartNo = "1172", QuantityOrdered = 5 },
			new { InvoiceLineID = "88a358a2822e4319b9b9", QuantityOrdered = 10, CustomFieldValues = new List<object> { new { SettingID = "1ae102b94dc54dfc8a45                ", Contents = "Adjustment requested by phone" } } }
		},
		Notes = new List<object>
		{
			new { NoteText = "Customer telephoned and asked for another few hours of labour" }
		}
	});

	responsebody = webClient.UploadString("https://api.jiwa.com.au/SalesOrders/babce67cdbf64f778536", "PATCH", json); 
}



Card
idSalesOrderPATCHRequest_curl
labelCurl


Code Block
languagebash
curl -H 'Accept: application/json' -H 'Content-Type: application/json' -X GET https://api.jiwa.com.au/auth -d '{"username":"Admin","password":"password"}'

Returns the following authentication response, containing the SessionId which subsequent requests will need to include in the cookie "ss-id"

No Format
{"SessionId":"6w1nLX8r0sIrJHClX9Vj","UserName":"Admin","DisplayName":"","ResponseStatus":{}}

Then, with the SessionId now known, the route can be called:

Code Block
languagebash
curl -H 'Accept: application/json' -H 'Content-Type: application/json' --cookie 'ss-id=6w1nLX8r0sIrJHClX9Vj' -X PATCH https://api.jiwa.com.au/SalesOrders/babce67cdbf64f778536 -d '{"ExpectedDeliveryDate":"2017-09-20","Lines":[{"PartNo":"1172","QuantityOrdered":5},{"InvoiceLineID":"88a358a2822e4319b9b9","QuantityOrdered":10,"CustomFieldValues":[{"SettingID":"1ae102b94dc54dfc8a45                ", "Contents":"Adjustment requested by phone"}]}],"Notes":[{"NoteText":"Customer telephoned and asked for another few hours of labour"}]}'




The response returned from the above request will be a json document representing the full sales order DTO model from the business logic - see the meta data page for the SalesOrderPATCHRequest for more detail.

Note(1): The DebtorID or DebtorAccountNo can be provided. If both are provided, then the DebtorID will be used to resolve the debtor.

Note(2): A sales order line can update an existing line by providing the InvoiceLineID property. New lines are appended by omitting InvoiceLineID.

Note(3): A new sales order line added can provide either the InventoryID, or the PartNo. If both are provided, then the InventoryID will be used to resolve the inventory item.

Note(4): When sales order lines are added, omitting the price will cause Jiwa to determine the price per normal pricing scheme logic.

Note(5): The last line added in the above example sets a custom line field value for a setting with ID "1ae102b94dc54dfc8a45"

Note(6): When adding notes if the NoteType is omitted, then the default note type configured in Jiwa is used.



Authenticating


Info
titleAPI Key Authentication

Jiwa 07.02.00 has added API Key authentication:

Jira Legacy
serverJiwa Service
columnskey,summary,type,created,updated,due,assignee,reporter,priority,status,resolution
serverIdf9231aec-a65a-32ae-94f3-7114fec5f253
keyDEV-6727

Jira Legacy
serverJiwa Service
columnskey,summary,type,created,updated,due,assignee,reporter,priority,status,resolution
serverIdf9231aec-a65a-32ae-94f3-7114fec5f253
keyDEV-6733

Most routes will require authentication. There are 2 methods of authentication - user credential and API Key. API Key authentication was introduced in version 7.2 of Jiwa.

User Credentials Authentication


User credentials authentication performed by invoking the /auth route and passing the username and password of a Jiwa user.  A successful authentication will return an authentication response containing the SessionId.  All subsequent requests will need to provide the SessionId as the value for the cookie "ss-id".

An alternative to using a cookie, is to send subsequent requests with the header "X-ss-id" set to the SessionId.

Panel
titleAuthenticate by calling the /auth route


Deck
idauthenticate


Card
idauthenticate_ss_csharp
labelServiceStack Client C#

See Installing the ServiceStackVS Extension for Visual Studio on how to configure your Visual Studio project to use the ServiceStack client needed for this example.

Code Block
languagec#
var client = new ServiceStack.JsonServiceClient("https://api.jiwa.com.au");
var authResponse = client.Get(new ServiceStack.Authenticate() { UserName = "admin", Password = "password" });


Card
idauthenticate_csharp
labelC#

Code Block
languagec#
using (var webClient = new System.Net.WebClient())
{
    // Authenticate               
    webClient.QueryString.Add("username", "Admin");
    webClient.QueryString.Add("password", "password");
     
    string responsebody = webClient.DownloadString("https://api.jiwa.com.au/auth");               
}


Card
idauthenticate_curl
labelCurl

Code Block
languagebash
curl -H 'Accept: application/json' -H 'Content-Type: application/json' -X GET https://api.jiwa.com.au/auth -d '{"username":"Admin","password":"password"}'


Card
idauthenticate_browser
labelWeb Browser

Navigate to the auth URL and provide the username and password as parameters:

No Format
https://api.jiwa.com.au/auth?username=admin&password=password&format=json

This authenticates the user and creates a cookie, so a subsequent request will automatically include the SessionId. Note the &format=json in the above URL this overrides the content type returned. For browsers the default content type is HTML - if a content type override is omitted, then a HTML razor view of the data will be returned instead of json. xml and csv are also valid overrides for the content type to be returned. We only used ?format=json in this example to demonstrate the return value.



The response returned from the above request:

No Format
{"SessionId":"6w1nLX8r0sIrJHClX9Vj","UserName":"Admin","DisplayName":"","ResponseStatus":{}}



API Key Authentication


API Keys can be provided either as a HTTP Bearer token in the request header or as a URL or form parameter. There is no authentication step with API Keys, you simply provide the API Key with the request being made.

Code Block
languagebash
titleHTTP Bearer token
curl -H 'Authorization: Bearer znis9fHm0_D5EGRrGdWG7WA2EP7Hke_gloC6R76A2t0' -H 'Accept: application/json' -H 'Content-Type: application/json' -X GET http://localhost/Debtors


Code Block
languagebash
titleURL Parameter
curl -H 'Accept: application/json' -H 'Content-Type: application/json' -X GET http://localhost/Debtors?apikey=znis9fHm0_D5EGRrGdWG7WA2EP7Hke_gloC6R76A2t0

There are 2 types of API Keys in Jiwa 7.2 - Staff and Debtor.

Staff API Keys

Staff API Keys are simply an alternate way of authenticating to the username and password. A staff member can have an API key associated with them via the Staff Maintenance form in Jiwa:

Debtor API Keys

Debtor API Keys are associated with a debtor via the API Keys tab of the Debtor Maintenance form:

Recommended Route Exposure

As the Debtor API key is expected to be given to a customer, care must be taken to only expose routes/services necessary and not to leak sensitive information.

By default the REST API plugin will filter some requests and responses to prevent unwanted information disclosure. The following lists which routes currently filter the requests and responses:

RouteSummaryVerbRequest Filtering PerformedResponse Filtering Performed
 /Inventory/{InventoryID}Retrieves an inventory itemGET  None
        • Removes costs
        • Removes debtor specific prices for other debtors
        • Removed debtor classifaction prices for other debtors
 /Queries/SalesOrderListRetrieves a list of sales orders GETFiltered on DebtorID None
 /SalesOrders/{InvoiceID}Retrieves a sales order GETFiltered on DebtorIDRemoves the following from sales order lines:
        • DiscountedPercentage
        • DiscountGiven
        • PriceExGst
        • TaxRate.LedgerAccount
        • UnitCost
        • Line Details Cost
        • Line Details SOHID
        • Line Details SpecialPrice
 /SalesOrders Creates a new sales order POSTDebtorID replaced Removes the following from sales order lines:
        • DiscountedPercentage
        • DiscountGiven
        • PriceExGst
        • TaxRate.LedgerAccount
        • UnitCost
        • Line Details Cost
        • Line Details SOHID
        • Line Details SpecialPrice
/Queries/SalesQuoteListRetrieves a list of sales quotesGETFiltered on DebtorID None
/SalesQuotes/{QuoteID}Retrieves a sales QuoteGET Filtered on DebtorIDRemoves the following from sales quote lines:
        • DiscountedPercentage
        • DiscountGiven
        • PriceExGst
        • UnitCost
/SalesQuotesCreates a new sales QuotePOST DebtorID replacedRemoves the following from sales quote lines:
        • DiscountedPercentage
        • DiscountGiven
        • PriceExGst
        • UnitCost
/Queries/DebtorTransactionListRetrieves a list of debtor transactionsGET Filtered on DebtorIDNone
/DebtorsRetrieves a debtor for a API Key authenticated customerGET None - DebtorID impliedRemoves the following:
        • Budgets
        • DebtorLedgers
        • Classification.DebtorLedgers


Example of calling a route


This example shows how to retrieve a debtor record for a known DebtorID.

Panel
titleRetreiving a debtor record from the /Debtors/\{DebtorID\} route


Deck
idretrieve_debtor


Card
iddebtor_ss_csharp
labelServiceStack Client C#

See Installing the ServiceStackVS Extension for Visual Studio on how to configure your Visual Studio project to use the ServiceStack client needed for this example.

Code Block
languagec#
var client = new ServiceStack.JsonServiceClient("https://api.jiwa.com.au");
var authResponse = client.Get(new ServiceStack.Authenticate() { UserName = "admin", Password = "password" });

var debtorGETResponse = client.Get(new DebtorGETRequest { DebtorID = "0000000061000000001V" });


Card
iddebtor_csharp
labelC#

Code Block
languagec#
using (var webClient = new System.Net.WebClient())
{
    // Authenticate               
    webClient.QueryString.Add("username", "Admin");
    webClient.QueryString.Add("password", "password");
     
    string responsebody = webClient.DownloadString("https://api.jiwa.com.au/auth");
    // Deserialise response into a dynamic - below requires the Newtonsoft.Json nuget package - or you can use any other json serialisation library
	var authResponse = Newtonsoft.Json.JsonConvert.DeserializeObject<dynamic>(responsebody);
	var sessionId = authResponse.SessionId;

	webClient.Headers.Add(System.Net.HttpRequestHeader.Cookie, string.Format("ss-id={0}", sessionId));                
	responsebody = webClient.DownloadString("https://api.jiwa.com.au/Debtors/0000000061000000001V");         
}


Card
iddebtor_curl
labelCurl

Code Block
languagebash
curl -H 'Accept: application/json' -H 'Content-Type: application/json' -X GET https://api.jiwa.com.au/auth -d '{"username":"Admin","password":"password"}'
curl -H 'Accept: application/json' -H 'Content-Type: application/json' --cookie 'ss-id=6w1nLX8r0sIrJHClX9Vj' -X GET https://api.jiwa.com.au/Debtors/0000000061000000001V


Card
iddebtor_browser
labelWeb Browser

Navigate to the auth URL and provide the username and password as parameters:

No Format
https://api.jiwa.com.au/auth?username=admin&password=password&format=json

Then navigate to the following URL:

No Format
https://api.jiwa.com.au/Debtors/0000000061000000001V




The response returned from the above request will be a json document representing the full debtor DTO model from the business logic - see the meta data page for the DebtorGETRequest for more detail.



 

Stateful Interaction


While typically interactions with a REST API would be stateless, it is possible to interact in a stateful way by passing the Request header "jiwa-stateful" with the value of "true".

When stateful requests are received, the server caches the appropriate business logic and subsequent requests will interact with that in-memory object.  This allows the consumer to perform actions like building a sales order without it being saved to the database until it is ready to save it.

Stateful requests will be committed to the database when a SAVE Request is received.  Pending changes can also be discarded with an ABANDON Request.

Below is an example of a stateful interaction with the Debtors - the object is statefully retrieved, and updated until a SAVE Request is sent.

Code Block
languagec#
titlestateful requests
var client = new ServiceStack.JsonServiceClient("http://localhost");
	var authResponse = client.Send<ServiceStack.AuthenticateResponse>(new ServiceStack.Authenticate()
	{
		provider = "credentials",
		UserName = "api",
		Password = "password",
		RememberMe = true
	});

	// Read a debtor
	client.Headers.Add("jiwa-stateful", "true");
	var debtorGETResponse = client.Get(new DebtorGETRequest { DebtorID = "0000000061000000001V" });

	// Update debtor 
	var debtorPATCHResponse  = client.Patch(new DebtorPATCHRequest() { DebtorID = "0000000061000000001V",  Name = "My new name", CreditLimit = 1000 });
 
    // Update some more fields
	debtorPATCHResponse  = client.Patch(new DebtorPATCHRequest() { DebtorID = "0000000061000000001V",  Address1 = "SE2L10 100 Walker Street" });

	// Save the changes
	var debtorSAVEResponse = client.Get(new DebtorSAVERequest() { DebtorID = "0000000061000000001V" });
}

 

When the server creates the business logic object, it is stored in a collection associated with the users session (this is actually a property of the Manager class - the ObjectDictionary).  Subsequent stateful requests for the same type (eg: Debtor Maintenance operations) will retrieve any existing business logic for the same record, otherwise a new business logic instance is created.

This means two subsequent stateful operations for different debtors will result in two business logic objects created by the server, and they will remain independent of each other.

Conversely, two subsequent stateful operations for the same debtor will result only one business logic object created by the server, and the second operation will be working on the same business logic instance as the first operation.


Related articles

Filter by label (Content by label)
showLabelsfalse
max5
spacesAPI
showSpacefalse
sortmodified
reversetrue
typepage
cqllabel in ("consume","restapi") and type = "page" and space = "J7UG"
labelsconsume c-sharp
Page Properties
hiddentrue

Related issues