Contact (contact)¶
contact is the standard Dataverse table for individual people. akoyaGO uses it directly — staff, donors, grant applicants, scholarship recipients, board members, and volunteers are all contact records. akoyaGO extends contact with custom columns under the akoya_ prefix and wires it into donor, gift, and request workflows.
For the full schema of the base contact entity, see the Microsoft Dataverse contact reference. This page focuses on what's specific to akoyaGO and what integration partners need to know.
At a glance
| Display name | Contact |
| Logical name | contact |
| Primary ID attribute | contactid |
| Primary name attribute | fullname (computed from firstname + lastname) |
| Entity set name (Web API) | contacts |
| Ownership | UserOwned |
| Change tracking | Enabled (standard) |
| Audit | Enabled (standard) |
Web API¶
GET {org}/api/data/v9.2/contacts
GET {org}/api/data/v9.2/contacts({contactid})
POST {org}/api/data/v9.2/contacts
PATCH {org}/api/data/v9.2/contacts({contactid})
The akoyaGO role of contact¶
Every physical person in akoyaGO is a contact. That person may also be linked from one or more of:
akoya_donor— viaakoya_primarycontactand/orakoya_secondarycontactlookups (see the constituent model).akoya_request— viaakoya_applicantid(required on grant/scholarship requests).akoya_gift— viaakoya_primarydonorcontact,akoya_secondarydonorcontact,akoya_payor, andakoya_memorialcontactlookups.akoya_fund— viaakoya_primaryfundcontactand fund advisor relationships.
One contact can participate in many of these roles simultaneously.
Notable akoyaGO-added columns¶
akoyaGO extends contact with columns under the akoya_ prefix. The columns most relevant to integration partners:
| Display name | Logical name | Type | Purpose |
|---|---|---|---|
| Active Address | akoya_activeaddress |
Choice | Which of the multi-address slots (home, business, seasonal) is currently active. |
| Spouse Email | akoya_spouseemail |
String | Spouse's email when the contact is part of a two-person household donor. |
| GOdonate Email Confirmed | akoya_godonateemailconfirmed |
Boolean | Set when the contact has confirmed an email via the GOdonate portal. Not an authoritative email-marketing opt-in flag — see the note below. |
Email-marketing consent — known gap
akoya_godonateemailconfirmed captures one specific portal confirmation, not general email-marketing consent. There is no authoritative email-preference / opt-in-date column on contact today. Integrations needing reliable consent state should coordinate with the akoyaGO product team before building. Tracked as a schema gap in the project implementation plan.
Standard columns most used in integrations¶
From the base contact entity, partners typically read:
| Logical name | Type | Notes |
|---|---|---|
firstname |
String | |
lastname |
String | |
fullname |
String (computed) | Concatenation — don't write directly. |
emailaddress1 |
String | Primary email. Use this for email-marketing / audience sync. |
emailaddress2 / emailaddress3 |
String | Secondary / tertiary. |
mobilephone |
String | |
telephone1 |
String | Primary phone. |
address1_line1 / _line2 / _city / _stateorprovince / _postalcode / _country |
String | Primary mailing address. |
birthdate |
DateTime (DateOnly) | |
statecode / statuscode |
Choice | Active / Inactive lifecycle. |
Lookups from contact to akoyaGO entities¶
contact is usually the referenced side, not the referencing side. To find the donor records that reference a contact:
curl "https://{org}.crm.dynamics.com/api/data/v9.2/akoya_donors?\$select=akoya_donorid,akoya_formaldefault&\$filter=_akoya_primarycontact_value%20eq%20$CONTACT_ID%20or%20_akoya_secondarycontact_value%20eq%20$CONTACT_ID" \
-H "Authorization: Bearer $ACCESS_TOKEN" \
-H "Accept: application/json" \
-H "OData-Version: 4.0" \
-H "OData-MaxVersion: 4.0"
var contactId = new Guid("...");
var query = new QueryExpression("akoya_donor") {
ColumnSet = new ColumnSet("akoya_donorid", "akoya_formaldefault"),
Criteria = {
FilterOperator = LogicalOperator.Or,
Conditions = {
new ConditionExpression("akoya_primarycontact", ConditionOperator.Equal, contactId),
new ConditionExpression("akoya_secondarycontact", ConditionOperator.Equal, contactId)
}
}
};
var donors = service.RetrieveMultiple(query);
Supported messages¶
| Message | Supported |
|---|---|
| Create | ✓ |
| Retrieve | ✓ |
| RetrieveMultiple | ✓ |
| Update | ✓ |
| Delete | ✓ |
| Upsert | — (no akoyaGO alternate keys; use GET then PATCH) |
Examples¶
Conventions
Examples assume ORG (env URL), access_token (valid bearer), headers (standard Dataverse headers dict), and service (a ServiceClient instance). See Authentication.
Find a contact by email¶
curl "https://{org}.crm.dynamics.com/api/data/v9.2/contacts?\$select=contactid,fullname,emailaddress1,akoya_godonateemailconfirmed&\$filter=emailaddress1%20eq%20'donor@example.org'&\$top=1" \
-H "Authorization: Bearer $ACCESS_TOKEN" \
-H "Accept: application/json" \
-H "OData-Version: 4.0" \
-H "OData-MaxVersion: 4.0"
var query = new QueryExpression("contact") {
ColumnSet = new ColumnSet("fullname", "emailaddress1", "akoya_godonateemailconfirmed"),
Criteria = {
Conditions = {
new ConditionExpression("emailaddress1", ConditionOperator.Equal, "donor@example.org")
}
},
TopCount = 1
};
var result = service.RetrieveMultiple(query);
var contact = result.Entities.FirstOrDefault();
Email-marketing audience sync query¶
Active contacts with a confirmed portal email, ready for an audience push to an external email-marketing platform. Pair with @odata.deltaLink for incremental delta sync.
GET {org}/api/data/v9.2/contacts?
$select=contactid,firstname,lastname,emailaddress1,akoya_godonateemailconfirmed&
$filter=statecode eq 0 and akoya_godonateemailconfirmed eq true and emailaddress1 ne null&
$orderby=modifiedon asc
Accept: application/json
OData-Version: 4.0
OData-MaxVersion: 4.0
Prefer: odata.track-changes
curl "https://{org}.crm.dynamics.com/api/data/v9.2/contacts?\$select=contactid,firstname,lastname,emailaddress1,akoya_godonateemailconfirmed&\$filter=statecode%20eq%200%20and%20akoya_godonateemailconfirmed%20eq%20true%20and%20emailaddress1%20ne%20null&\$orderby=modifiedon%20asc" \
-H "Authorization: Bearer $ACCESS_TOKEN" \
-H "Accept: application/json" \
-H "OData-Version: 4.0" \
-H "OData-MaxVersion: 4.0" \
-H "Prefer: odata.track-changes"
r = requests.get(
f"{ORG}/api/data/v9.2/contacts",
params={
"$select": "contactid,firstname,lastname,emailaddress1,akoya_godonateemailconfirmed",
"$filter": "statecode eq 0 and akoya_godonateemailconfirmed eq true and emailaddress1 ne null",
"$orderby": "modifiedon asc",
},
headers={**headers, "Prefer": "odata.track-changes"},
)
body = r.json()
contacts = body["value"]
delta_link = body.get("@odata.deltaLink") # persist for the next incremental call
var query = new QueryExpression("contact") {
ColumnSet = new ColumnSet("firstname", "lastname", "emailaddress1", "akoya_godonateemailconfirmed"),
Criteria = {
Conditions = {
new ConditionExpression("statecode", ConditionOperator.Equal, 0),
new ConditionExpression("akoya_godonateemailconfirmed", ConditionOperator.Equal, true),
new ConditionExpression("emailaddress1", ConditionOperator.NotNull)
}
},
Orders = { new OrderExpression("modifiedon", OrderType.Ascending) }
};
var contacts = service.RetrieveMultiple(query);
// For incremental delta sync, use RetrieveEntityChangesRequest instead.
Create a contact and link to a donor¶
Two-step: POST the contact, then PATCH the donor's primary-contact lookup.
POST {org}/api/data/v9.2/contacts
Accept: application/json
OData-Version: 4.0
OData-MaxVersion: 4.0
Content-Type: application/json
{
"firstname": "Jane",
"lastname": "Smith",
"emailaddress1": "jane.smith@example.org"
}
Then:
CONTACT_RESP=$(curl -s -i -X POST "https://{org}.crm.dynamics.com/api/data/v9.2/contacts" \
-H "Authorization: Bearer $ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-H "OData-MaxVersion: 4.0" \
-H "OData-Version: 4.0" \
-d '{
"firstname": "Jane",
"lastname": "Smith",
"emailaddress1": "jane.smith@example.org"
}')
CONTACT_ID=$(echo "$CONTACT_RESP" | grep -i '^OData-EntityId' | sed -E 's/.*\(([^)]+)\).*/\1/' | tr -d '\r')
curl -X PATCH "https://{org}.crm.dynamics.com/api/data/v9.2/akoya_donors({donorid})" \
-H "Authorization: Bearer $ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-H "If-Match: *" \
-d "{\"akoya_PrimaryContact@odata.bind\": \"/contacts($CONTACT_ID)\"}"
r = requests.post(
f"{ORG}/api/data/v9.2/contacts",
json={
"firstname": "Jane",
"lastname": "Smith",
"emailaddress1": "jane.smith@example.org",
},
headers={**headers, "Content-Type": "application/json"},
)
# OData-EntityId header contains the new contact URL — extract the GUID.
contact_id = r.headers["OData-EntityId"].rsplit("(", 1)[1].rstrip(")")
requests.patch(
f"{ORG}/api/data/v9.2/akoya_donors({donor_id})",
json={"akoya_PrimaryContact@odata.bind": f"/contacts({contact_id})"},
headers={**headers, "Content-Type": "application/json", "If-Match": "*"},
)
var contact = new Entity("contact");
contact["firstname"] = "Jane";
contact["lastname"] = "Smith";
contact["emailaddress1"] = "jane.smith@example.org";
var contactId = service.Create(contact);
var donor = new Entity("akoya_donor", donorId);
donor["akoya_primarycontact"] = new EntityReference("contact", contactId);
service.Update(donor);
Change history¶
akoyaGO-specific extension columns extracted from the Akoyanet solution on 2026-04-24. Base contact columns are not listed here — partners should reference Microsoft's Dataverse documentation for those.