Foreign IDs
Abound enables developers to associate a record with a unique, customer-specific ID, known as a foreignId
. This allows developers to forego making their own tables every time they integrate and/or add an app.
Uniqueness
Foreign IDs must be unique per resource entity (expense, income, payer, user). If a foreign id is not unique, a 409 Error will occur.
When creating expenses and incomes in batch processes, if a 409 error occurs, nothing from the batch will be created. If this happens, the error body will include an array of conflictingIds
.
Below are two example error responses of batch creations failing:
// Duplicate IDs in the same request body
{
"message": "Duplicate foreignIds provided",
"request": {
"timestamp": 1650466385740,
"requestId": "requestId_XXXXXXXXXXXXXXXXX"
},
"conflictingIds": [
"123"
]
}
// Attempting to create/update record with foreign ID that is already tied to another record
{
"message": "Records containing these foreignIds already exist",
"request": {
"timestamp": 1650466449573,
"requestId": "requestId_XXXXXXXXXXXXXXXXX"
},
"conflictingIds": [
"123",
"234"
]
}
Examples
Foreign IDs are always located at the root of our data object. In our example, we will be using the Users endpoint, but you can expect this to work the same for all other supported endpoints.
Here is an example of creating a User with a foreignId
:
curl \
--request POST \
--url https://sandbox-api.withabound.com/<<apiVersion>>/users \
--header 'Authorization: Bearer <<apiKey>>' \
--header 'Content-Type: application/json' \
--data '{
"user": {
"email": "[email protected]",
"foreignId": "your_foreign_id",
"profile": {
"firstName": "Ada",
"lastName": "Lovelace",
"address": "256 Byron Street",
"address2": "Suite 32",
"city": "Palo Alto",
"state": "CA",
"zipcode": "94306",
"country": "US",
"phoneNumber": "6505551010",
"dateOfBirth": "1815-12-10",
"socialSecurityNumber": "101456789"
}
}
}'
const { default: Abound, Environment } = require("@withabound/node-sdk");
const abound = new Abound({
appId: "<<sandbox_app_id>>",
appSecret: "<<sandbox_app_secret>>",
environment: Environment.SANDBOX,
apiVersion: "<<apiVersion>>",
});
(async () => {
const response = await abound.users.create({
email: "[email protected]",
foreignId: "your_foreign_id",
profile: {
firstName: "Ada",
lastName: "Lovelace",
address: "256 Byron Street",
address2: "Suite 32",
city: "Palo Alto",
state: "CA",
zipcode: "94306",
country: "US",
phoneNumber: "6505551010",
dateOfBirth: "1815-12-10",
socialSecurityNumber: "101456789",
},
});
console.log(response);
})();
import requests
url = "https://sandbox-api.withabound.com/<<apiVersion>>/users"
payload = {"user": {
"profile": {
"firstName": "Ada",
"lastName": "Lovelace",
"address": "256 Byron Street",
"address2": "Suite 32",
"city": "Palo Alto",
"state": "CA",
"zipcode": "94306",
"country": "US",
"phoneNumber": "6505551010",
"dateOfBirth": "1815-12-10",
"socialSecurityNumber": "101456789"
},
"email": "[email protected]"
"foreignId": "your_foreign_id"
}}
headers = {
"Accept": "application/json",
"Content-Type": "application/json",
"Authorization": "Bearer <<apiKey>>"
}
response = requests.request("POST", url, json=payload, headers=headers)
print(response.text)
// import com.squareup.okhttp.*;
// import com.google.gson.JsonObject;
OkHttpClient client = new OkHttpClient();
JsonObject requestBody = new JsonObject();
JsonObject user = new JsonObject();
JsonObject profile = new JsonObject();
user.addProperty("email", "[email protected]");
user.addProperty("foreignId", "your_foreign_id");
profile.addProperty("firstName", "Ada");
profile.addProperty("lastName", "Lovelace");
profile.addProperty("addPropertyress", "256 Byron Street");
profile.addProperty("addPropertyress2", "Suite 32");
profile.addProperty("city", "Palo Alto");
profile.addProperty("state", "CA");
profile.addProperty("zipcode", "94306");
profile.addProperty("country", "US");
profile.addProperty("phoneNumber", "6505551010");
profile.addProperty("dateOfBirth", "1815-12-10");
profile.addProperty("socialSecurityNumber", "101456789");
user.add("profile", profile);
requestBody.add("user", user);
MediaType mediaType = MediaType.parse("application/json");
RequestBody body = RequestBody.create(mediaType, requestBody.toString());
Request request = new Request.Builder()
.url("https://sandbox-api.withabound.com/<<apiVersion>>/users")
.post(body)
.addHeader("Accept", "application/json")
.addHeader("Content-Type", "application/json")
.addHeader("Authorization", "Bearer <<apiKey>>")
.build();
Response response = client.newCall(request).execute();
package main
import (
"bytes"
"fmt"
"net/http"
"io/ioutil"
)
func main() {
url := "https://sandbox-api.withabound.com/<<apiVersion>>/users"
var requestBody = []byte(`{
"user": {
"email": "[email protected]",
"foreignId": "your_foreign_id",
"profile": {
"firstName": "Ada",
"lastName": "Lovelace",
"address": "256 Byron Street",
"address2": "Suite 32",
"city": "Palo Alto",
"state": "CA",
"zipcode": "94306",
"country": "US",
"phoneNumber": "6505551010",
"dateOfBirth": "1815-12-10",
"socialSecurityNumber": "101456789"
}
}
}`)
req, _ := http.NewRequest("POST", url, bytes.NewBuffer(requestBody))
req.Header.Add("Accept", "application/json")
req.Header.Add("Content-Type", "application/json")
req.Header.Add("Authorization", "Bearer <<apiKey>>")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := ioutil.ReadAll(res.Body)
fmt.Println(res)
fmt.Println(string(body))
}
// using RestSharp;
var client = new RestClient("https://sandbox-api.withabound.com/<<apiVersion>>/users");
var request = new RestRequest(Method.POST);
request.AddHeader("Accept", "application/json");
request.AddHeader("Content-Type", "application/json");
request.AddHeader("Authorization", "Bearer <<apiKey>>");
request.AddJsonBody(new {
user = new {
email = "[email protected]",
foreignId = "your_foreign_id",
profile = new {
firstName = "Ada",
lastName = "Lovelace",
address = "256 Byron Street",
address2 = "Suite 32",
city = "Palo Alto",
state = "CA",
zipcode = "94306",
country = "US",
phoneNumber = "6505551010",
dateOfBirth = "1815-12-10",
socialSecurityNumber = "101456789"
}
}
});
IRestResponse response = client.Execute(request);
require 'uri'
require 'net/http'
require 'openssl'
require 'json'
url = URI("https://sandbox-api.withabound.com/<<apiVersion>>/users")
requestBody = {
user: {
email: '[email protected]',
foreignId: 'your_foreign_id',
profile: {
firstName: 'Ada',
lastName: 'Lovelace',
address: '256 Byron Street',
address2: 'Suite 32',
city: 'Palo Alto',
state: 'CA',
zipcode: '94306',
country: 'US',
phoneNumber: '6505551010',
dateOfBirth: '1815-12-10',
socialSecurityNumber: '101456789'
}
}
}
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Accept"] = 'application/json'
request["Content-Type"] = 'application/json'
request["Authorization"] = 'Bearer <<apiKey>>'
request.body = requestBody.to_json
response = http.request(request)
puts response.read_body
Next, here is an example of updating the foreignId
on an existing User, uniqueness requirements also apply in updating entities:
curl \
--request PUT \
--url https://sandbox-api.withabound.com/<<apiVersion>>/users/<<testUserId>> \
--header 'Authorization: Bearer <<apiKey>>' \
--header 'Content-Type: application/json' \
--data '{
"user": {
"email": "[email protected]",
"foreignId": "your_foreign_id"
}
}'
const { default: Abound, Environment } = require("@withabound/node-sdk");
const abound = new Abound({
appId: "<<sandbox_app_id>>",
appSecret: "<<sandbox_app_secret>>",
environment: Environment.SANDBOX,
apiVersion: "<<apiVersion>>",
});
(async () => {
const response = await abound.users.update(
"<<testUserId>>",
{
email: "[email protected]",
foreignId: "your_foreign_id",
}
);
console.log(response);
})();
import requests
url = "https://sandbox-api.withabound.com/<<apiVersion>>/users/<<testUserId>>"
payload = {"user": {
"email": "[email protected]",
"foreignId": "your_foreign_id"
}}
headers = {
"Accept": "application/json",
"Content-Type": "application/json",
"Authorization": "Bearer <<apiKey>>"
}
response = requests.request("PUT", url, json=payload, headers=headers)
print(response.text)
import com.withabound.*;
import com.withabound.models.*;
import com.withabound.resources.base*;
AboundConfig aboundConfig = new AboundConfig(
"<<sandbox_app_id>>",
"<<sandbox_app_secret>>",
AboundEnvironment.SANDBOX,
AboundApiVersion.<<apiVersionUppercase>>
);
Abound abound = new Abound(aboundConfig);
String userId = "<<testUserId>>";
UserRequest userRequest = UserRequest.builder()
.email("[email protected]")
.foreignId("your_foreign_id")
.build();
AboundResponse<User> response = abound.users().update(userId, userRequest);
System.out.println(response.getData());
package main
import (
"bytes"
"fmt"
"net/http"
"io/ioutil"
)
func main() {
url := "https://sandbox-api.withabound.com/<<apiVersion>>/users/<<testUserId>>"
var requestBody = []byte(`{
"user": {
"email": "[email protected]",
"foreignId": "your_foreign_id"
}
}`)
req, _ := http.NewRequest("PUT", url, bytes.NewBuffer(requestBody))
req.Header.Add("Accept", "application/json")
req.Header.Add("Content-Type", "application/json")
req.Header.Add("Authorization", "Bearer <<apiKey>>")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := ioutil.ReadAll(res.Body)
fmt.Println(res)
fmt.Println(string(body))
}
// using RestSharp;
var client = new RestClient("https://sandbox-api.withabound.com/<<apiVersion>>/users");
var request = new RestRequest(Method.PUT);
request.AddHeader("Accept", "application/json");
request.AddHeader("Content-Type", "application/json");
request.AddHeader("Authorization", "Bearer <<apiKey>>");
request.AddJsonBody(new {
user = new {
email = "[email protected]",
foreignId = "your_foreign_id"
}
});
IRestResponse response = client.Execute(request);
require 'uri'
require 'net/http'
require 'openssl'
require 'json'
url = URI("https://sandbox-api.withabound.com/<<apiVersion>>/users/<<testUserId>>")
requestBody = {
user: {
email: '[email protected]',
foreignId: 'your_foreign_id'
}
}
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Put.new(url)
request["Accept"] = 'application/json'
request["Content-Type"] = 'application/json'
request["Authorization"] = 'Bearer <<apiKey>>'
request.body = requestBody.to_json
response = http.request(request)
puts response.read_body
And finally, here is an example of retrieving a User with the foreignId
query parameter filter:
curl \
--request GET \
--url 'https://sandbox-api.withabound.com/<<apiVersion>>/users?foreignId=your_foreign_id' \
--header 'Authorization: Bearer <<apiKey>>'
const { default: Abound, Environment } = require("@withabound/node-sdk");
const abound = new Abound({
appId: "<<sandbox_app_id>>",
appSecret: "<<sandbox_app_secret>>",
environment: Environment.SANDBOX,
apiVersion: "<<apiVersion>>",
});
(async () => {
const response = await abound.users.list({
foreignId: "your_foreign_id",
});
console.log(response);
})();
import requests
url = "https://sandbox-api.withabound.com/<<apiVersion>>/users"
querystring = {"foreignId":"your_foreign_id"}
headers = {
"Accept": "application/json",
"Authorization": "Bearer <<apiKey>>"
}
response = requests.request("GET", url, headers=headers, params=querystring)
print(response.text)
import com.withabound.*;
import com.withabound.models.*;
import com.withabound.resources.base*;
AboundConfig aboundConfig = new AboundConfig(
"<<sandbox_app_id>>",
"<<sandbox_app_secret>>",
AboundEnvironment.SANDBOX,
AboundApiVersion.<<apiVersionUppercase>>
);
Abound abound = new Abound(aboundConfig);
UserParams params = UserParams.builder().foreignId("your_foreign_id").build();
AboundBulkResponse<User> response = abound.users().list(params);
System.out.println(response.getData());
package main
import (
"fmt"
"net/http"
"io/ioutil"
)
func main() {
url := "https://sandbox-api.withabound.com/<<apiVersion>>/users?foreignId=your_foreign_id"
req, _ := http.NewRequest("GET", url, nil)
req.Header.Add("Accept", "application/json")
req.Header.Add("Authorization", "Bearer <<apiKey>>")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := ioutil.ReadAll(res.Body)
fmt.Println(res)
fmt.Println(string(body))
}
// using RestSharp;
var client = new RestClient("https://sandbox-api.withabound.com/<<apiVersion>>/users?foreignId=your_foreign_id");
var request = new RestRequest(Method.GET);
request.AddHeader("Accept", "application/json");
request.AddHeader("Authorization", "Bearer <<apiKey>>");
IRestResponse response = client.Execute(request);
require 'uri'
require 'net/http'
require 'openssl'
url = URI("https://sandbox-api.withabound.com/<<apiVersion>>/users?foreignId=your_foreign_id")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Get.new(url)
request["Accept"] = 'application/json'
request["Authorization"] = 'Bearer <<apiKey>>'
response = http.request(request)
puts response.read_body
See below for an example of how to handle foreignID conflicts in a batch call.
Updated 11 days ago