25

This question is related to Microsoft Dynamics CRM 2015, that I'm calling through API.

I create contact entity:

POST [organization URI]/api/data/contacts
Content-Type: application/json; charset=utf-8
Accept: application/json
{
    "emailaddress1": "[email protected]",
}

It works, I see new record, after I log into the panel. And I can call it through the API:

[organization URI]/api/data/contacts(f76e4e7c-ea61-e511-80fd-3863bb342b00)
{
  "@odata.context":"[organization URI]/api/data/$metadata#contacts/$entity",
  "@odata.etag":"W/\"460199\"",
  ...
  "contactid":"f76e4e7c-ea61-e511-80fd-3863bb342b00",
  "emailaddress1":"[email protected]",
  ....
}

Next thing I want to do, is to add annotation record associated with that contact. Following the guide I call:

POST [organization URI]/api/data/annotations
Content-Type: application/json; charset=utf-8
Accept: application/json
{
    "notetext": "TEST",
    '[email protected]': 'contacts(f76e4e7c-ea61-e511-80fd-3863bb342b00)'
}

But it returns 400 error:

An undeclared property 'contact' which only has property annotations in the payload but no property value was found in the payload. In OData, only declared navigation properties and declared named streams can be represented as properties without values.

When I call:

POST [organization URI]/api/data/annotations
Content-Type: application/json; charset=utf-8
Accept: application/json
{
    "notetext": "TEST",
}

New entity is created, but without a relation to contact.

How to properly compose this POST request? What am I missing here? I suspect, that [email protected] should be presented somehow different, I've tried [email protected], [email protected], [email protected] - but no effects.

Any ideas?

3
  • why are you using the web API preview and not the REST endpoint? Commented Sep 24, 2015 at 13:08
  • the code should be "[email protected]": "/contacts(f76e4e7c-ea61-e511-80fd-3863bb342b00)" note the / before contacts and the quotation marks (just in case) Commented Sep 24, 2015 at 13:14
  • 1
    @GuidoPreite I've tried and the error message is: A property 'objectid' which only has property annotations in the payload but no property value is declared to be of type 'Edm.Guid'. In OData, only navigation properties and named streams can be represented as properties without values.
    – maicher
    Commented Sep 24, 2015 at 13:25

8 Answers 8

24

Instead of using [email protected], you have to use [email protected]. This results are in:

"[email protected]": "/contacts(f76e4e7c-ea61-e511-80fd-3863bb342b00)"

To get the list of properties, look under the single-valued navigation properties in the documentation.

5
  • 3
    This is better than the accepted response since it only uses 1 call. I'm guessing the addition of "_contact" (or something similar) depends on if the current record is a parent or child.
    – zabby
    Commented Feb 16, 2016 at 16:28
  • 1
    I believe that it is because this is specific to the regarding field on notes, which can lookup to various entities. Therefore, the _contact is dependent on which entity you wish to set it regarding (always objectid_entityname for notes regarding field).
    – Justin
    Commented Feb 16, 2016 at 19:53
  • 1
    Indeed the property logical name and the navigation property name are not consistent so checking the docs is essential. Additionally I had to drop the leading slash in the value so to specify a value for "businessunitid" I had to use: "[email protected]": "businessunits(b6c0d6c6-a07f-e611-80e8-c4346bc49e60)".
    – Nathan
    Commented Apr 30, 2019 at 23:38
  • 1
    In addition to all of that, I found that the case of the attributes is important too. For example, I had "[email protected]" (which failed) vs "[email protected]" (which is how it is reflected in the CRM GUI..and this worked).
    – Mr Moose
    Commented Apr 4, 2020 at 14:01
  • I had to use the field "Schema name" which is CASE SENTIVE! Commented Aug 29, 2022 at 15:48
10
+250

Part 1:
MS Docs Reference: Deep Insert

You can create entities related to each other by defining them as navigation properties values. This is known as deep insert. As with a basic create, the response OData-EntityId header contains the Uri of the created entity. The URIs for the related entities created aren’t returned.

Below code is to create Account (1), create + associate Primary contact (2), create & Associate Opportunity (3) and create + associate Task (4)

POST [Organization URI]/api/data/v8.2/accounts HTTP/1.1
Content-Type: application/json; charset=utf-8
OData-MaxVersion: 4.0
OData-Version: 4.0
Accept: application/json

{
 "name": "Sample Account",
 "primarycontactid":
 {
     "firstname": "John",
     "lastname": "Smith"
 },
 "opportunity_customer_accounts":
 [
  {
      "name": "Opportunity associated to Sample Account",
      "Opportunity_Tasks":
      [
       { "subject": "Task associated to opportunity" }
      ]
  }
 ]
}

Part 2:
Associating annotation to contact uses the below syntax.

note["[email protected]"] = "/contacts(C5DDA727-B375-E611-80C8-00155D00083F)";

Refer SO link & blog

Part 3:
Answer to your comment on another answer about annotation_id_from_first_request:

To get the created record Id in response from last request, you can parse like below:

                //get Response from Created Record
                entityIdWithLink = XMLHttpRequest.getResponseHeader("OData-EntityId");
 
                //get EntityId from ResponseHeader of Created Record  
                getEntityId = entityIdWithLink.split(/[()]/);
                getEntityId = getEntityId[1];

You can read more

You can compose your POST request so that data from the created record will be returned with a status of 201 (Created).
To get this result, you must use the return=representation preference in the request headers. To control which properties are returned, append the $select query option to the URL to the entity set.
The $expand query option will be ignored if used. When an entity is created in this way the OData-EntityId header containing the URI to the created record is not returned

Note: This capability was added with December 2016 update for Dynamics 365

MS Docs Reference: Create with data returned

Update:
If anyone looking for working payload sample to deep insert a record + annotation, the below is from my project:

data = {
        "new_attribute1": "test attribute 1",
        "new_attribute2": "test attribute 2",
        "new_comments": "test comments",
        "new_recordurl": recordURL,
        "new_feedback_Annotations":
            [
                {
                    "notetext": "Screenshot attached",
                    "subject": "Attachment",
                    "filename": file.name,
                    "mimetype": file.type,
                    "documentbody": base64str,
                }
            ]
    };
9

I've found this working, but in two requests:

POST [organization URI]/api/data/annotations
Content-Type: application/json; charset=utf-8
Accept: application/json
{
    "notetext": "TEST"
}

POST [organization URI]/api/data/contacts(f76e4e7c-ea61-e511-80fd-3863bb342b00)/Contact_Annotation/$ref
Content-Type: application/json; charset=utf-8
Accept: application/json
{
    "@odata.id": "[organization URI]/annotations(annotation_id_from_first_request)"
}

Edit:

annotation_id_from_first_request value is taken form first request's response.

1
  • From the first request's response.
    – maicher
    Commented Aug 17, 2017 at 11:47
5

This answer applies for web api usage:

If the references property has been defined using uppercase letters, you have to use uppercase letters in the property on update and insert. Look at the Schema name in the property list of the primary entity.

Lets say you have an entity called myprefix_entity with a reference to the account entity, and you named it Account, and the schema name became myprefix_AccountId, you would have to refer it as:

"[email protected]":"/accounts(f76e4e7c-ea61-e511-80fd-000000000000)"

The uppercase A and the uppercase I in myprefix_AccountId matters, if that is how the schema name has been defined.

1
  • This is also an issue with the Ribbon Workbench for Dynamics CRM
    – Christo
    Commented Jul 2, 2021 at 12:11
4

I'm using this C# Code for creating and linking (the Task.Await stuff is not very clever, so... be careful):

dynamic testAno = new ExpandoObject();
testAno.NoteText = "Hello World!";
testAno.Subject = "Note Subject";

dynamic refAccount = new ExpandoObject();
refAccount.LogicalName = "account";
refAccount.Id = "003CCFC2-4012-DE11-9654-001F2964595C";

testAno.ObjectId = refAccount;
testAno.ObjectTypeCode = refAccount.LogicalName;

var demo = JsonConvert.SerializeObject(testAno);

HttpContent content = new StringContent(demo, Encoding.UTF8, "application/json");

var handler = new HttpClientHandler { UseDefaultCredentials = true };

HttpClient client = new HttpClient(handler);
var test = client.PostAsync(new Uri("http://crm/.../XRMServices/2011/OrganizationData.svc/AnnotationSet"), content).Result;

The JSON is looking like this:

{"NoteText":"Hello World!",
 "Subject":"Note Subject",
 "ObjectId": {"LogicalName":"account",
              "Id":"003CCFC2-4012-DE11-9654-001F2964595C"}
,"ObjectTypeCode":"account"}
1

You can use following.

'[email protected]': '/contacts(f76e4e7c-ea61-e511-80fd-3863bb342b00)'

In most of the record you will get _contactid_value as a parameter name. So you have to pass like [email protected] as a parameter and in the value you have to pass 'EntitySetName' which would be contacts and GUID. '/EntitysetName(GUID)' So the value will be '/contacts(f76e4e7c-ea61-e511-80fd-3863bb342b00)'

0

might be a bit late for this, but the answer in the following link explains how the binding works really well.

basically, you need to use the field schema name with the suffix @odata.bind and the value being "/entityschemaname(recordGUID)" good to remember that the entityschemaname needs to have an 's' and the recordGUID should not have the curly brackets.

for more information follow this link below where i got this information from

'An undeclared property' when trying to create record via Web API

0

If you use OData, then you can do it this way...

In your Annotation data model:

[DataContract(Name = "annotations")]
public record Annotation
{
    [DataMember(Name = "objectid_rd_servicerequestsession")]
    public ServiceRequestSession ObjectId { get; set; } = default!;

    [DataMember(Name = "objecttypecode")]
    public string ObjectTypeCode { get; set; } = default!;

Where the rd_servicerequestsession is your entity name. Then you just need to create a new object Annotation object

    var annotation = new Annotation
    {
        ObjectId = serviceRequestSession,
        ObjectTypeCode = "rd_servicerequestsession",

And simply invoke InsertEntry method.

Not the answer you're looking for? Browse other questions tagged or ask your own question.