Skip to main content

REST API CRUD Requests

You can use the REST API to build apps for taking advantage of data residency services provided by InCountry. This page documents the REST resources provided by the InCountry Data Residency-as-a-Service (DRaaS), along with the expected HTTP response codes and sample requests.

InCountry REST API is a set of RESTful methods that allow you to create, update, query and delete regulated data from the InCountry Data Residency-as-a-Service (DRaaS). It simplifies data communication and lets you quickly build applications with data localization capabilities.

The data flow diagram is shown below:

REST API Diagram

You can make CRUD requests through REST API to access regulated data stored on the InCountry platform.

note

Executing calls from the frontend (browser) requires an additional layer of authorization checks to ensure that the current user is allowed to perform the requested operation on the specified resources. For more details, please πŸ“ƒ refer to the documentation.

Prerequisites​

  1. Create a new REST API service.

  2. Create Data Schema.

    For this guide, assume a schema named Users is defined as follows:

       id: Primary Key
    first_name: String
    last_name: String
    email: String
    phone: String
    yearly_spending: Integer
    deals_ratio: Decimal
    is_active: Boolean
    last_login: Date&Time
    last_operation: Date
    notification_time: Time
  3. Copy details of the REST API integration, as follows:

    1. Environment ID

    2. Client ID

    3. Client Secret

    4. REST API address

      REST API integration

  4. Get an access token to authenticate in REST API

Beginning now, consider that we have a schema named "users" with the indexed fields: first_name, last_name, and passport.

Creating a New Record​

POST /api/models/{schemaName}

Let's say we want to add a new user. To create a record send a POST request to /api/models/{schemaName} path.

Request body

Please see the example of the request body structure.

Example

{
"id": "user1_primary_key",
"first_name": "John",
"last_name": "Smith",
"email": "john.smith@mail.com",
"phone": "25534512",
"yearly_spending": 24000,
"deals_ratio": 3.5,
"is_active": true,
"last_login": "2022-04-03T12:00:13.300Z",
"last_operation": "2023-07-17",
"notification_time": "11:30:00",
"expires_at": "2027-04-07T07:15:44.393Z"
}

Here, we are creating a new user.

cURL request

curl --location --globoff 'https://{restApiURLAddress}/api/users' \
--header 'Authorization: Bearer {ACCESS_TOKEN}' \
--header 'Content-Type: application/json' \
--data '{
"id": "user1_primary_key",
"first_name": "John",
"last_name": "Smith",
"email": "john.smith@mail.com",
"phone": "25534512",
"yearly_spending": 24000,
"deals_ratio": 3.5,
"is_active": true,
"last_login": "2022-04-03T12:00:13.300Z",
"last_operation": "2023-07-17",
"notification_time": "11:30:00",
"expires_at": "2027-04-07T07:15:44.393Z"
}'

tip

Pay attention that every record must have a unique record_key across the environment (within the same environment_id). It serves as a primary key, enabling you to access, modify, or delete your record.

Responses

STATUS 201 - application/json A record has been created successfully. Returns a record key and record details similar to the data you submitted within the request.

Example

{
"id": "user1_primary_key",
"first_name": "John",
"last_name": "Smith",
"email": "john.smith@mail.com",
"phone": "25534512",
"yearly_spending": 24000,
"deals_ratio": 3.5,
"is_active": true,
"last_login": "2022-04-03T12:00:13.300Z",
"last_operation": "2023-07-17",
"notification_time": "11:30:00",
"created_at": <creation date>,
"updated_at": <creation date>
}

STATUS 400 - application/json An invalid record has been provided.

STATUS 401 - Access is denied.

STATUS 409 - InCountry Vault for the country is not available.

STATUS 415 - An unsupported request content type.

STATUS 422 - Validation of input parameters has failed.

STATUS 429 - The number of allowed requests has been exceeded.

STATUS 5** - Server error.

Creating Multiple Records​

POST /api/models/{schemaName}/batch

There is a way to create multiple record in one request.

Please see the example of the request structure.

Example

[
{
"id": "user1_primary_key",
"first_name": "John",
"last_name": "Smith",
"email": "john.smith@mail.com",
"phone": "25534512",
"yearly_spending": 24000,
"deals_ratio": 3.5,
"isActive": true,
"last_login": "2022-04-03T12:00:13.300Z",
"last_operation": "2023-07-17",
"notification_time": "11:30:00",
"expires_at": "2027-04-07T07:15:44.393Z"
},
{
"id": "user2_primary_key",
"first_name": "Jack",
"last_name": "Doe",
"email": "jack.doe@mail.com",
"phone": "31345623",
"yearly_spending": 67000,
"deals_ratio": 4.5,
"is_active": true,
"last_login": "2023-07-17T14:30:13.500Z",
"last_operation": "2022-08-18",
"notification_time": "10:30:00",
"expires_at": "2026-02-07T07:15:44.393Z"
}
]

Now we're adding 2 more users to InCountry Storage.

cURL request

curl --location --globoff 'https://{restApiURLAddress}/api/models/users/batch' \
--header 'Authorization: Bearer {ACCESS_TOKEN}' \
--header 'Content-Type: application/json' \
--data '{
"country": "se",
[
{
"id": "user1_primary_key",
"first_name": "John",
"last_name": "Smith",
"email": "john.smith@mail.com",
"phone": "25534512",
"yearly_spending": 24000,
"deals_ratio": 3.5,
"is_active": true,
"last_login": "2022-04-03T12:00:13.300Z",
"last_operation": "2023-07-17",
"notification_time": "11:30:00",
"expires_at": "2027-04-07T07:15:44.393Z",
},
{
"id": "user2_primary_key",
"first_name": "Jack",
"last_name": "Doe",
"email": "jack.doe@mail.com",
"phone": "31345623",
"yearly_spending": 67000,
"deals_ratio": 4.5,
"is_active": true,
"last_login": "2023-07-17T14:30:13.500Z",
"last_operation": "2022-08-18",
"notification_time": "10:30:00",
"expires_at": "2026-02-07T07:15:44.393Z",
}
]

note

There is a limit for the number of records in one batch, which is set to 1000.

Responses

STATUS 201 - application/json Records have been created successfully. Returns record keys and record details similar to data you're submitting within the request payload.

Example

[
{
"id": "user1_primary_key",
"first_name": "John",
"last_name": "Smith",
"email": "john.smith@mail.com",
"phone": "25534512",
"yearly_spending": 24000,
"deals_ratio": 3.5,
"is_active": true,
"last_login": "2022-04-03T12:00:13.300Z",
"last_operation": "2023-07-17",
"notification_time": "11:30:00",
"created_at": <creation date>,
"updated_at": <creation date>
},
{
"id": "user2_primary_key",
"first_name": "Jack",
"last_name": "Doe",
"email": "jack.doe@mail.com",
"phone": "31345623",
"yearly_spending": 67000,
"deals_ratio": 4.5,
"is_active": true,
"last_login": "2023-07-17T14:30:13.500Z",
"last_operation": "2022-08-18",
"notification_time": "10:30:00",
"created_at": <creation date>,
"updated_at": <creation date>
}
]

STATUS 400 - application/json Invalid records have been provided.

STATUS 401 - Access is denied.

STATUS 409 - InCountry Vault for the country is not available.

STATUS 415 - An unsupported request content type.

STATUS 422 - Records cannot be processed.

STATUS 429 - The number of allowed requests has been exceeded.

STATUS 5** - Server error.

Updating Multiple Records​

POST /api/models/{schemaName}/batch/update

Also, it is possible to update several records at once using this endpoint.

The structure of the request is identical to the request to create multiple records; if there are records in the country with the specified record primary leys, they will be updated rather than new ones added.

Example

[
{
"id": "user1_primary_key",
"first_name": "John",
"last_name": "Smith",
"email": "john.smith.new@mail.com",
"phone": "25534512",
"yearly_spending": 24000,
"deals_ratio": 3.4,
"isActive": true,
"last_login": "2022-04-03T12:00:13.300Z",
"last_operation": "2024-07-17",
"notification_time": "11:30:00",
"expires_at": "2027-04-07T07:15:44.393Z"
},
{
"id": "user2_primary_key",
"first_name": "Jack",
"last_name": "Doe",
"email": "jack.doe.new@mail.com",
"phone": "31345623",
"yearly_spending": 67000,
"deals_ratio": 4.6,
"is_active": true,
"last_login": "2023-07-17T14:30:13.500Z",
"last_operation": "2024-08-18",
"notification_time": "10:30:00",
"expires_at": "2026-02-07T07:15:44.393Z"
}
]

Now we're updating these users in InCountry Storage.

cURL request

curl --location --globoff 'https://{restApiURLAddress}/api/models/users/batch/update' \
--header 'Authorization: Bearer {ACCESS_TOKEN}' \
--header 'Content-Type: application/json' \
--data '{
"country": "se",
[
{
"id": "user1_primary_key",
"first_name": "John",
"last_name": "Smith",
"email": "john.smith.new@mail.com",
"phone": "25534512",
"yearly_spending": 24000,
"deals_ratio": 3.4,
"is_active": true,
"last_login": "2022-04-03T12:00:13.300Z",
"last_operation": "2024-07-17",
"notification_time": "11:30:00",
"expires_at": "2027-04-07T07:15:44.393Z",
},
{
"id": "user2_primary_key",
"first_name": "Jack",
"last_name": "Doe",
"email": "jack.doe.new@mail.com",
"phone": "31345623",
"yearly_spending": 67000,
"deals_ratio": 4.6,
"is_active": true,
"last_login": "2023-07-17T14:30:13.500Z",
"last_operation": "2024-08-18",
"notification_time": "10:30:00",
"expires_at": "2026-02-07T07:15:44.393Z",
}
]

Responses

STATUS 201 - application/json Records have been created successfully. Returns record keys and record details similar to data you're submitting within the request payload.

Example

[
{
"id": "user1_primary_key",
"first_name": "John",
"last_name": "Smith",
"email": "john.smith.new@mail.com",
"phone": "25534512",
"yearly_spending": 24000,
"deals_ratio": 3.5,
"is_active": true,
"last_login": "2022-04-03T12:00:13.300Z",
"last_operation": "2023-07-17",
"notification_time": "11:30:00",
"created_at": <creation date>,
"updated_at": <creation date>
},
{
"id": "user2_primary_key",
"first_name": "Jack",
"last_name": "Doe",
"email": "jack.doe.new@mail.com",
"phone": "31345623",
"yearly_spending": 67000,
"deals_ratio": 4.6,
"is_active": true,
"last_login": "2023-07-17T14:30:13.500Z",
"last_operation": "2022-08-18",
"notification_time": "10:30:00",
"created_at": <creation date>,
"updated_at": <creation date>
}
]

STATUS 400 - application/json Invalid records have been provided.

STATUS 401 - Access is denied.

STATUS 409 - InCountry Vault for the country is not available.

STATUS 415 - An unsupported request content type.

STATUS 422 - Records cannot be processed.

STATUS 429 - The number of allowed requests has been exceeded.

STATUS 5** - Server error.

note

While updating, as well as while creating, there is a limit for the number of records in one batch, which is set to 1000.

note

On update, it is possible to customize the request: you can pass the field search_key by which records will be updated and specify which fields to return in the response by specifying return_keys. For example, the following request will update records whose first_name is equal to John, setting deals_ratio to 3.9 and return only first_name and deals_ratio in the response:

curl --location --globoff 'https://{restApiURLAddress}/api/models/users/batch/update' \
--header 'Authorization: Bearer {ACCESS_TOKEN}' \
--header 'Content-Type: application/json' \
--data '{
"country": "se",
"search_key": "first_name",
"return_keys": ["first_name", "deals_ratio"],
"records": [
{
"first_name": "John",
"deals_ratio": 3.9,
}
]
}'

Response:

[
{
"first_name": "John",
"deals_ratio": 3.9
}
]

Duplicating a Single Record​

POST /api/models/{schemaName}/clone

This request can duplicate a record as-is or modify the duplicated record on the fly.

Request body

Please review the example of the request body structure. It includes a section for specifying filters to identify duplicate records and another section for specifying the values for updating a record.

Example

{
"filter": {
"first_name": "John"
},
"update_with": {
"id": "primary_key_for_a_new_user",
"email": "anotheremailvalue@mail.com"
}
}

cURL request

curl --location --globoff 'https://{restApiURLAddress}/api/users/clone' \
--header 'Authorization: Bearer {ACCESS_TOKEN}' \
--header 'Content-Type: application/json' \
--data '{
"filter": {
"first_name": "John",
},
"update_with": {
"id": "primary_key_for_a_new_user",
"email": "anotheremailvalue@mail.com"
}
}'

Responses

STATUS 201 - The record has been duplicated successfully.

STATUS 400 - Invalid input parameters have been provided for record lookup or duplication.

STATUS 401 - Access is denied.

STATUS 403 - Partial text-match search is not enable for InCountry Vault.

STATUS 403 - No records matching the filtration criteria have been found.

STATUS 409 - InCountry Vault for the country is not supported.

STATUS 415 - An unsupported request content type.

STATUS 422 - Decryption of records matching the filtration criteria has failed.

STATUS 429 - The number of allowed requests has been exceeded.

STATUS 5** - Server error.

Searching for Records​

POST /api/models/{schemaName}/find

Now that we have some records available in the Storage, let's execute a search to find some of them. There is a find endpoint that allows you to specify search criteria. Let's say we want to retrieve users with the yearly_spending field set in the range of 11000 to 30000.

Please see the example of the request structure.

Example

{
"filter": {
"yearly_spending": {"$gte":11000, "$lte":30000},
},
"options": {
"limit": 10,
"offset": 0
}
}

cURL request

curl --location --globoff 'https://{restApiURLAddress}/api/users/find' \
--header 'Authorization: Bearer {ACCESS_TOKEN}' \
--header 'Content-Type: application/json' \
--data '{
"filter": {
"yearly_spending": {"$gte":60000, "$lte":90000}
},
"options": {
"limit": 10,
"offset": 0
}
}'

Responses

STATUS 200 - The records has been found successfully.

Let the request will give us two users matching the criteria. The response will look as follows:

Example

{
"data": [
{
"id": "user2_primary_key",
"first_name": "Jack",
"last_name": "Doe",
"email": "jack.doe@mail.com",
"phone": "31345623",
"yearly_spending": 67000,
"deals_ratio": 4.5,
"is_active": true,
"last_login": "2023-07-17T14:30:13.500Z",
"last_operation": "2022-08-18",
"notification_time": "10:30:00",
"attachments": [],
"created_at": "2021-03-26T11:57:45.000Z",
"updated_at": "2021-03-26T11:57:45.000Z"
},
{
"id": "user3_primary_key",
"first_name": "Mike",
"last_name": "Johnson",
"email": "mike.johnson@mail.com",
"phone": "22544444",
"yearly_spending": 73000,
"deals_ratio": 1.5,
"is_active": true,
"last_login": "2021-07-17T14:30:13.500Z",
"last_operation": "2021-07-17",
"notification_time": "12:30:00",
"attachments": [],
"created_at": "2021-03-26T11:57:45.000Z",
"updated_at": "2021-03-26T11:57:45.000Z"
}
],
"meta": {
"count": 2,
"limit": 10,
"offset": 0,
"total": 2
}
}

STATUS 400 - Invalid filter or options.

STATUS 401 - Access is denied.

STATUS 403 - Search by keys is not available for this storage.

STATUS 409 - InCountry Vault for the country is not available.

STATUS 415 - An unsupported request content type.

STATUS 422 - A record cannot be processed.

STATUS 429 - The number of allowed requests has been exceeded.

STATUS 5** - Server error.

You can find more information about using find endpoint in the reference.

Deleting a Specific Record​

DELETE /api/models/{schemaName}/{country}/{record_key}

Request parameters

ParametersTypeValueDescription
countryISO country code (lowercase)sgCountry where the record is stored.
record_keystringk1234Unique primary key (record_key or its data schema mapping) for record identification

Now, let's delete a record. To do so, we need to use the record_key of the record we want to delete. For example, let's delete a record with the key user1_primary_key.

cURL request

curl --request DELETE \
--url 'https://{restApiURLAddress}/api/users/{country}/{record_key}' \
--header 'Authorization: Bearer {ACCESS_TOKEN}' \

//URL EXAMPLE
https://se-restapi-mt-01.incountry.io/api/users/se/user1_primary_key

Responses

STATUS 204 - plain The record has been successfully deleted.

STATUS 400 - Invalid query parameters have been specified.

STATUS 401 - Access is denied.

STATUS 404 - The record has not been found.

STATUS 409 - InCountry Vault for the country is not available.

STATUS 429 - The number of allowed requests has been exceeded.

STATUS 5** - Server error.

This endpoint doesn't respond with a body.

Deleting Multiple Records​

POST /api/models/{schemaName}/batch/delete

Request body

ParametersTypeExampleDescription
filterobjectSee the example.Specify the criteria for deletion within theΒ filterΒ object. Currently only deletion by record_key is supported.
record_keyarraySee the example.Array of record keys or its data schema mappings that are subject to removal.

Please see the example of the request structure.

Example

{
"filter": {
"id": [
"user_id_1", "user_id_2"
]
}
}

cURL request

curl --request POST \
--url 'https://{restApiURLAddress}/api/models/users/batch/delete' \
--header 'Authorization: Bearer {ACCESS_TOKEN}' \
--header 'Content-Type: application/json' \
--data '{
"filter": {
"id": [
"user_id_1",
"user_id_2"
]
}

Responses

STATUS 204 - plain Records have been successfully deleted.

STATUS 400 - An invalid input has been provided.

STATUS 401 - Access is denied.

STATUS 404 - No records matching filtration criteria have been found.

STATUS 409 - InCountry Vault for the country is not available.

STATUS 415 - An unsupported request content type.

STATUS 422 - An unsupported request content type.

STATUS 429 - The number of allowed requests has been exceeded.

STATUS 5** - Server error.

Attaching a File​

It is possible to attach a file to a record.

note

Keep in mind that files cannot exist separately and should always be attached to some record. Deleting a record will result in the deletion of all the files attached to it.

Example:

curl --request POST \
--url 'https://in-restapi-mt-01.api.incountry.io/api/users/in/user2_primary_key/files' \
--header 'Authorization: Bearer ory_at_05MJ4MHOkRhW0A0wduM8Ehr_cRsk5acakP6u4dn2rFo.23THKavIs_PHKMR8W-AxaPmEq10ymWQC_fttHio_FlE' \
--header 'Content-Type: multipart/form-data; boundary=---011000010111000001101001' \
--header 'x-filename: expenses.pdf' \
--form 'file=@"/documents/expenses.pdf'

In this example we attached a file expenses.pdf to a record with record_key="user2_primary_key". In the response you will get information about attachment:

{
"file_id": "5416a334-6ce6-4e3b-9a0d-464cb417c094",
"filename": "expenses.pdf",
"hash": "78701b482fc9738e26f32254bbe7d71990f5025a9b728046a280a3822cdf5cad",
"mime_type": "application/x-sh",
"size": 279,
"created_at": "2021-03-26T11:36:44.000Z",
"updated_at": "2021-03-26T11:36:44.000Z",
"download_link": "https://in-mt-01.incountry.io/v2/storage/records/in/310459560d9e757d6e51be45a5aad862fbb81c52d72a026e75d21366ac223a40/attachments/5416a334-6ce6-4e3b-9a0d-464cb417c094"
}

The file_id field is the attachment's unique identifier and is used to access and modify the attachment.

Please see File Management for more details on how to implement file attachments CRUD operations.

Downloading an Attached File​

curl --request GET \
--url 'https://{restApiUrl}/api/models/{schemaName}/{country}/{record_key}/files/{file_id}' \
--header 'Authorization: Bearer {ACCESS_TOKEN}'

In our case, the request will be the following:

curl --request GET \
--url 'https://in-restapi-mt-01.api.incountry.io/api/users/in/user2_primary_key/files/5416a334-6ce6-4e3b-9a0d-464cb417c094' \
--header 'Authorization: Bearer ory_at_05MJ4MHOkRhW0A0wduM8Ehr_cRsk5acakP6u4dn2rFo.23THKavIs_PHKMR8W-AxaPmEq10ymWQC_fttHio_FlE'

Please see File Management for more details on how to implement file attachments CRUD operations.

Deleting an Attached File​

To delete an attachment, execute a DELETE request and specify record_key and file_id.

The request template is as follows:

curl --request DELETE \
--url 'https://{restApiURLAddress}/api/models/{schemaName}/{country}/{id}/files/{file_id}' \
--header 'Authorization: Bearer {ACCESS_TOKEN}' \

In our case, the request will be the following:

curl --request DELETE \
--url 'https://in-restapi-mt-01.api.incountry.io/api/users/in/user2_primary_key/files/5416a334-6ce6-4e3b-9a0d-464cb417c094' \
--header 'Authorization: Bearer ory_at_05MJ4MHOkRhW0A0wduM8Ehr_cRsk5acakP6u4dn2rFo.23THKavIs_PHKMR8W-AxaPmEq10ymWQC_fttHio_FlE'

Please see File Management for more details on how to implement file attachments CRUD operations.

Fine Grained Access​

RestAPI provides an additional set of endpoints (/webapi) that can be used to implement an additional layer of authorization. It can be used to implement requests from a front-end application.

Please see the docs for details.