The Activity API is Village's most commonly used API.
An “Activity” represents something your users do. It is the a basic unit of data that can represent any sort of user activity both online and in the real world. Activities can be anything from completing a transaction, clicking a button, or making a delivery.
Activities are the foundation of most Rule logic. Even with a relatively small number of Activities, you can build incredibly powerful and intricate incentive & segmentation system.
We recommend that you log as many Activity types that you can up front to reduce maintenance effort (see Guides->).
Defining Activities
You can send Activities to the Activity API to be logged on the Village ledger (making them queryable) without adding them first on the Admin Dashboard.
However, until they are added, they will not be usable in Program & Rule logic.
To be used in Program & Rule logic, Activities must be added in the Admin Dashboard -> Incentives -> Triggers -> Actions/Sales. Any Admin can complete this action.
If an Activity is sent through the Activity API without having been defined on the Admin Dashboard, it will still be logged on the Village ledger (and therefore queryable in the future). It will not be usable in Program & Rule logic until it is added, however.
Note there are two types of Activities: Actions and Sales (learn more here ->). However the Activity API is agnostic to the type; any logic based on Actions or Sales will be based on the Activity Short ID field.
Endpoint
POST/networks/YOUR_NETWORK_ID/activity
Where 'YOUR_NETWORK_ID' is replaced with your actual Network ID.
API Field Overview
Every Activity has an amount field, in which you can report the volume of activities (example: 1 checkout, 3 deliveries, 4.45 hours) or the degree of an activity (example: 5 star rating, 3 star rating).
When Activities are created as Actions and Sales, they can reference multiple users and include metadata for tracking and reporting.
Pay attention to the amount field: most of the time, the amount field is going to be set to 1, since 1 event or activity of that type occurred. If you have a rule in Village that pays $50 when a task is completed, the rule will multiply the task activity amount (1) by $50 to calculate the payout. In this case, if you entered 50 in the activity amount field, this would result in a payout of 50 * $50 = $2500!
The exception to this rule is when you are configuring sales triggers. In this case, the amount field should be the total amount of the transaction or sale, eg. 500 if it was a $500 sale. For example, if a rule pays 1% cash back on each sale, it would multiply the rule amount field (.01) by the sales activity amount field ($500) to get a cash back of $5 for that sale.
Body Fields
Field Name
JSON Key
Type
Description
Required
Activity Short ID
activity_short_id
string
CASE SENSITIVE. A short identifier for the activity. This will match either the Action ID or Sale ID that was set when the Action on Sale trigger type was created.
Yes
Amount
amount
string
The activity amount. For Actions, this is the number of actions. For Sales, this is the sale transaction amount.
Yes
Associated Users
users
map
List of users involved in the activity. This map has two parts, first is one of the pre-set "Associated Users" from the Trigger (e.g. "seller") and the second is the email associated with their Village account (e.g. "bradford@villagelabs.co"). At least one Associated User is required to be submitted with every Activity.
Yes
Metadata
metadata
object
Additional metadata for the activity. See Metadata fields for options.
No
About Associated Users
Metadata
Field Name
JSON Key
Type
Description
Required
Reference ID
reference_id
string
An optional identifier that can be used for reporting purposes.
No
Activity Timestamp
activity_timestamp
integer
The Unix timestamp of when the activity occurred (in seconds, not milliseconds). If this is blank, Village will use the timestamp the activity was received via the Village API as the Activity Timestamp.
No
Description
description
string
A description of the activity.
No
Enforce Unique Ref ID
enforceUniqueRefId
boolean
Enforces a unique reference ID to process Activities.
Examples
Body
Critical note: The activity_short_id is case sensitive, and must exactly match the Trigger created on the dashboard in order to be processed.
// Example Activity Body
// activity_short_id is case sensitive
// User emails must already exist in Village system to be accepted
// activity_timestamp is Unix timestamp in seconds, not milliseconds
{
"activity_short_id": "sale",
"amount": "3",
"users": {
"buyer": "bradford@theroutingcompany.com",
"seller": "bradford@villagelabs.co"
},
"metadata": {
"reference_id": "customer_ref_id",
"activity_timestamp": 12345678910, // Epoch time in seconds
"description": "customer description"
},
"enforceUniqueRefId": false, // Enforces the the reference_id to be unique, and does not process requests that aren't unique. There is no error message when this activates
}
By Language
#Remember to replace 'YOUR_API_KEY' with your actual API key#Replace 'YOUR_NETWORK_ID' with your actual network IDimport requestsimport json# Define the URL of the User Status APIurl ="https://api-ledger.villagelabs.net/networks/YOUR_NETWORK_ID/activity"# Define the headers for the API requestheaders ={'Content-Type':'application/json','Accept':'application/json','Authorization':'Bearer YOUR_API_KEY'}# Define the data for the API requestdata ={"activity_short_id":"S1-A","amount":"100.99","users":{"seller":"john.buyer@villagelabs.co","buyer":"jane.seller@villagelabs.co"},"metadata":{"reference_id":"dpi_Ylo2Cfr8US8u1JIdAl2eZvKB","activity_timestamp":1664900628,"description":"Especially fancy white shoes."}}# Convert the data to JSON formatdata_json = json.dumps(data)# Send the request to the APIresponse = requests.post(url, headers=headers, data=data_json)# Check the response from the APIif response.status_code ==201:print("Successfully sent activity data to the API.")elif response.status_code ==400:print("Bad request. Please check the data you are sending to the API.")elif response.status_code ==401:print("Unauthorized. Please check your API key.")elif response.status_code ==404:print("API not found. Please check your API url.")elif response.status_code ==500:print("Internal Server Error. Please try again later.")else:print(f"Received unexpected status code {response.status_code}.")
//Remember to replace 'YOUR_API_KEY' with your actual API key//Replace 'YOUR_NETWORK_ID' with your actual network ID// Define the URL of the Activity APIlet url ="https://api-ledger.villagelabs.net/networks/YOUR_NETWORK_ID/activity";// Define the headers for the API requestlet headers = {'Content-Type':'application/json','Accept':'application/json','Authorization':'Bearer YOUR_API_KEY'};// Define the data for the API requestlet data = {"activity_short_id":"S1-A","amount":"100.99","users": {"seller":"john.buyer@villagelabs.co","buyer":"jane.seller@villagelabs.co" },"metadata": {"reference_id":"dpi_Ylo2Cfr8US8u1JIdAl2eZvKB","activity_timestamp":1664900628,"description":"Especially fancy white shoes." }};// Send the request to the APIfetch(url, { method:'POST', headers: headers, body:JSON.stringify(data) // JavaScript has JSON.stringify() to convert object into JSON}).then(response => {switch(response.status) {case201:console.log("Successfully sent activity data to the API.");break;case400:console.log("Bad request. Please check the data you are sending to the API.");break;case401:console.log("Unauthorized. Please check your API key.");break;case404:console.log("API not found. Please check your API url.");break;case500:console.log("Internal Server Error. Please try again later.");break;default:console.log(`Received unexpected status code ${response.status}.`); }}).catch(error =>console.error('Error:', error));
//Remember to replace 'YOUR_API_KEY' with your actual API key//Replace 'YOUR_NETWORK_ID' with your actual network IDcurl -X POST https://api-ledger.villagelabs.net/networks/YOUR_NETWORK_ID/activity \-H "Content-Type: application/json" \-H "Accept: application/json" \-H "Authorization: Bearer YOUR_API_KEY" \-d '{"activity_short_id":"S1-A","amount":"100.99","users": {"seller":"john.buyer@villagelabs.co","buyer":"jane.seller@villagelabs.co" },"metadata": {"reference_id":"dpi_Ylo2Cfr8US8u1JIdAl2eZvKB","activity_timestamp":1664900628,"description":"Especially fancy white shoes." }}'
#Remember to replace 'YOUR_API_KEY' with your actual API key#Replace 'YOUR_NETWORK_ID' with your actual network IDrequire'net/http'require'uri'require'json'# Define the URL of the Activity APIuri =URI.parse("https://api-ledger.villagelabs.net/networks/YOUR_NETWORK_ID/activity")# Define the headers for the API requestheaders = {'Content-Type'=>'application/json','Accept'=>'application/json','Authorization'=>'Bearer YOUR_API_KEY'# Replace 'Bearer YOUR_API_KEY' with your actual API key}# Define the data for the API requestdata = {"activity_short_id"=>"S1-A","amount"=>"100.99","users"=> {"seller"=>"john.buyer@villagelabs.co","buyer"=>"jane.seller@villagelabs.co" },"metadata"=> {"reference_id"=>"dpi_Ylo2Cfr8US8u1JIdAl2eZvKB","activity_timestamp"=>1664900628,"description"=>"Especially fancy white shoes." }}# Create a new HTTP request with the headers, data and URLhttp =Net::HTTP.new(uri.host, uri.port)http.use_ssl =truerequest =Net::HTTP::Post.new(uri.request_uri, headers)request.body = data.to_json# Send the request and capture the responseresponse = http.request(request)# Check the response from the APIcase response.code.to_iwhen201puts"Successfully sent activity data to the API."when400puts"Bad request. Please check the data you are sending to the API."when401puts"Unauthorized. Please check your API key."when404puts"API not found. Please check your API url."when500puts"Internal Server Error. Please try again later."elseputs"Received unexpected status code #{response.code}."end
//Remember to replace 'YOUR_API_KEY' with your actual API key//Replace 'YOUR_NETWORK_ID' with your actual network IDimportjava.io.OutputStream;importjava.net.HttpURLConnection;importjava.net.URL;publicclassMain {publicstaticvoidmain(String[] args) throwsException {String url ="https://api-ledger.villagelabs.net/networks/YOUR_NETWORK_ID/activity";URL obj =newURL(url);HttpURLConnection con = (HttpURLConnection) obj.openConnection();con.setRequestMethod("POST");//add request headercon.setRequestProperty("Content-Type","application/json");con.setRequestProperty("Accept","application/json");con.setRequestProperty("Authorization","Bearer YOUR_API_KEY");String data ="{\"activity_short_id\":\"S1-A\",\"amount\":\"100.99\",\"users\":{\"seller\":\"john.buyer@villagelabs.co\",\"buyer\":\"jane.seller@villagelabs.co\"},\"metadata\":{\"reference_id\":\"dpi_Ylo2Cfr8US8u1JIdAl2eZvKB\",\"activity_timestamp\":1664900628,\"description\":\"Especially fancy white shoes.\"}}";// Send post requestcon.setDoOutput(true);OutputStream os =con.getOutputStream();os.write(data.getBytes());os.flush();os.close();int responseCode =con.getResponseCode();System.out.println("Response Code : "+ responseCode); }}
// Remember to replace 'YOUR_API_KEY' with your actual API key//Replace 'YOUR_NETWORK_ID' with your actual network IDpackagemainimport ("bytes""net/http")funcmain() { url :="https://api-ledger.villagelabs.net/networks/YOUR_NETWORK_ID/activity"var jsonStr = []byte(`{"activity_short_id":"S1-A","amount":"100.99","users":{"seller":"john.buyer@villagelabs.co","buyer":"jane.seller@villagelabs.co"},"metadata":{"reference_id":"dpi_Ylo2Cfr8US8u1JIdAl2eZvKB","activity_timestamp":1664900628,"description":"Especially fancy white shoes."}}`) req, _ := http.NewRequest("POST", url, bytes.NewBuffer(jsonStr)) req.Header.Set("Content-Type", "application/json") req.Header.Set("Accept", "application/json") req.Header.Set("Authorization", "Bearer YOUR_API_KEY") client :=&http.Client{} resp, _ := client.Do(req)defer resp.Body.Close()println("response Status:", resp.Status)}