{"id":26761706,"url":"https://github.com/xrm-oss/xrm-webapi-client","last_synced_at":"2025-04-08T03:18:58.700Z","repository":{"id":37915385,"uuid":"70933390","full_name":"XRM-OSS/Xrm-WebApi-Client","owner":"XRM-OSS","description":"A promise-based JavaScript library for the Microsoft Dynamics CRM WebApi - TypeScript type definitions available","archived":false,"fork":false,"pushed_at":"2024-02-09T14:03:19.000Z","size":3231,"stargazers_count":100,"open_issues_count":6,"forks_count":28,"subscribers_count":24,"default_branch":"master","last_synced_at":"2025-04-08T03:18:46.113Z","etag":null,"topics":["crm","dynamics-365","dynamics-crm","dynamics-crm-webapi","javascript","typescript","webapi","webapiclient","xrm"],"latest_commit_sha":null,"homepage":"https://xrm-oss.github.io/Xrm-WebApi-Client/","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/XRM-OSS.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null}},"created_at":"2016-10-14T17:43:27.000Z","updated_at":"2024-09-30T10:08:21.000Z","dependencies_parsed_at":"2024-02-09T15:30:20.610Z","dependency_job_id":"d74e509f-708c-414e-b1b8-90db786fe284","html_url":"https://github.com/XRM-OSS/Xrm-WebApi-Client","commit_stats":{"total_commits":229,"total_committers":6,"mean_commits":"38.166666666666664","dds":0.4541484716157205,"last_synced_commit":"0052e1e5a74a5019cd4e16d6f9a127058b91310f"},"previous_names":[],"tags_count":81,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/XRM-OSS%2FXrm-WebApi-Client","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/XRM-OSS%2FXrm-WebApi-Client/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/XRM-OSS%2FXrm-WebApi-Client/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/XRM-OSS%2FXrm-WebApi-Client/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/XRM-OSS","download_url":"https://codeload.github.com/XRM-OSS/Xrm-WebApi-Client/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247767239,"owners_count":20992548,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["crm","dynamics-365","dynamics-crm","dynamics-crm-webapi","javascript","typescript","webapi","webapiclient","xrm"],"created_at":"2025-03-28T18:26:52.616Z","updated_at":"2025-04-08T03:18:58.668Z","avatar_url":"https://github.com/XRM-OSS.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Dynamics CRM JavaScript Web API Client\n[![Run Tests](https://github.com/XRM-OSS/Xrm-WebApi-Client/actions/workflows/npm-test.yml/badge.svg)](https://github.com/XRM-OSS/Xrm-WebApi-Client/actions/workflows/npm-test.yml) [![Coverage Status](https://coveralls.io/repos/github/XRM-OSS/Xrm-WebApi-Client/badge.svg?branch=master)](https://coveralls.io/github/XRM-OSS/Xrm-WebApi-Client?branch=master) [![Package Quality](http://npm.packagequality.com/shield/xrm-webapi-client.svg)](http://packagequality.com/#?package=xrm-webapi-client) [![npm downloads](https://img.shields.io/npm/dt/xrm-webapi-client.svg)](http://npm-stats.org/#/xrm-webapi-client) [![Join the chat at https://gitter.im/DigitalFlow/Xrm-WebApi-Client](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/DigitalFlow/Xrm-WebApi-Client?utm_source=badge\u0026utm_medium=badge\u0026utm_campaign=pr-badge\u0026utm_content=badge)\n\nThis is a framework for easing working with the Dynamics CRM WebApi using JavaScript.\nIt uses the awesome [BlueBird](https://github.com/petkaantonov/bluebird) framework for handling requests asynchronously based on promises.\nThe framework is supposed to be executed on CRM forms or on CRM web ressources, where the CRM context is available.\nIn addition to that, usage for single page applications outside of CRM is also possible.\nFor running from custom web resources, be sure that the GetGlobalContext function is available, as the client will try to retrieve the context on its own.\n\n# Index\n\n- [Dynamics CRM JavaScript Web API Client](#dynamics-crm-javascript-web-api-client)\n  * [Requirements](#requirements)\n    + [CRM](#crm)\n    + [Power Pages](#power-pages)\n    + [Browser](#browser)\n  * [How to obtain it](#how-to-obtain-it)\n    + [NPM](#npm)\n    + [GitHub Release](#github-release)\n  * [How to build it](#how-to-build-it)\n  * [Import](#import)\n  * [Operations](#operations)\n    + [Synchronous vs Asynchronous](#synchronous-vs-asynchronous)\n    + [Create](#create)\n      - [Return created record in create response](#return-created-record-in-create-response)\n    + [Retrieve](#retrieve)\n      - [Retrieve single records](#retrieve-single-records)\n        * [Retrieve by ID](#retrieve-by-id)\n        * [Retrieve by alternate key](#retrieve-by-alternate-key)\n      - [Retrieve multiple records](#retrieve-multiple-records)\n        * [Retrieve by query expression](#retrieve-by-query-expression)\n        * [Retrieve by FetchXml](#retrieve-by-fetchxml)\n      - [Auto expand collection-valued navigation properties](#auto-expand-collection-valued-navigation-properties)\n    + [Update](#update)\n      - [Update by alternate key](#update-by-alternate-key)\n      - [Return updated record in update response](#return-updated-record-in-update-response)\n      - [Clear lookup value](#clear-lookup-value)\n    + [Delete](#delete)\n      - [Delete by alternate key](#delete-by-alternate-key)\n      - [Delete single property](#delete-single-property)\n    + [Associate](#associate)\n    + [Disassociate](#disassociate)\n    + [Execute](#execute)\n      - [No parameter request](#no-parameter-request)\n      - [Parametrized request](#parametrized-request)\n      - [Component Types](#component-types)\n    + [Send Batch](#send-batch)\n      - [How to create batch requests](#how-to-create-batch-requests)\n      - [Batch Responses](#batch-responses)\n      - [Request failures](#request-failures)\n    + [Configuration](#configuration)\n    + [Errors](#errors)\n    + [Set Names](#set-names)\n    + [Not yet implemented requests](#not-yet-implemented-requests)\n    + [Promises](#promises)\n  * [External Access](#external-access)\n    + [Single Page Application](#single-page-application)\n  * [Headers](#headers)\n    + [Header Format](#header-format)\n    + [Default Headers](#default-headers)\n    + [Request Headers](#request-headers)\n      - [Page size](#page-size)\n    + [API Version](#api-version)\n  * [Remarks](#remarks)\n    + [CRM App](#crm-app)\n  * [FAQ](#faq)\n    + [Payloads](#payloads)\n    + [Logical Names](#logical-names)\n    + [MultiSelect Optionsets](#multiselect-optionsets)\n    + [Alternate Key with Lookup property](#alternate-key-with-lookup-property)\n\n## Requirements\n### CRM\nThis framework targets the Dynamics CRM WebApi, therefore CRM 2016 (\u003e= v8.0) is needed. \n\n### Power Pages\nPower Pages aka PowerApps portals are supported since v4.1.0.\n\nTo use this package on Power Pages, be sure to set the ClientUrl property once initially before issuing requests, like below:\n\n```JavaScript\nWebApiClient.ClientUrl = \"/_api/\";\n```\n\nFor TypeScript you currently have to use a workaround in v4.1.0, we're working on a fix:\n```TypeScript\n(WebApiClient as any).ClientUrl = \"/_api/\";\n```\n\nFor sending updates for Power Pages WebAPI, be sure to also set the CSRF token as default request header once initially:\n```TypeScript\nconst csrfToken = await shell.getTokenDeferred();\n\nWebApiClient.AppendToDefaultHeaders ({ key: \"__RequestVerificationToken\", value: csrfToken });\n\nawait WebApiClient.Update({ entityName: \"account\", entityId: \"c73d9add-dead-beef-babe-0022489a947a\", entity: { name: \"Update from WA\" } })\n```\n\n### Browser\nAlthough using Promises, some legacy browsers are still supported, since bluebird is used as Promise polyfill.\nBluebird is automatically included in the bundled release, no additional steps required.\nFor a list of supported browsers, check the bluebird [platform support](http://bluebirdjs.com/docs/install.html#supported-platforms).\n\n## How to obtain it\n### NPM\nThis framework is on npm as UMD, thanks to the standalone option of browserify.\n\nThe package name is xrm-webapi-client, check it out:\n\n[![NPM version](https://img.shields.io/npm/v/xrm-webapi-client.svg?style=flat)](https://www.npmjs.com/package/xrm-webapi-client)\n\nWhen using it in TypeScript, import it using this line:\n`import * as WebApiClient from \"xrm-webapi-client\";`\n\nIn your tsconfig.json, `module` should be `es6` and `moduleResolution` should be `node`.\n\n### GitHub Release\nYou can always download the browserified version of this framework by downloading the release.zip file from the latest [release](https://github.com/DigitalFlow/Xrm-WebApi-Client/releases).\n\n## How to build it\nYou'll have to install [npm](https://www.npmjs.com/) on your machine.\n\nFor bootstrapping, simply run ```npm install``` once initially.\nFor every build, you can just call ```npm run build```. You'll find the build output in the Publish directory.\n\n## Import\nYou can import this library inside your code like this:\n```JS\nimport * as WebApiClient from \"xrm-webapi-client\";\n```\n\n## Operations\n### Synchronous vs Asynchronous\nPer default, all requests are sent asynchronously.\nThis is the suggested way of sending requests, however, sometimes there is the need for using synchronous requests.\n\n\n\nBe sure to avoid synchronous requests if it is possible and use asynchronous requests instead.\n\nFor sending requests synchronously, you can either set ```WebApiClient.Async``` to false, which will configure the WebApiClient to send all requests synchronously, or pass an ```async``` property in your request, like so:\n\n```JavaScript\nvar request = {\n    entityName: \"account\",\n    entity: {name: \"Adventure Works\"},\n    async: false\n};\n\ntry {\n    var response = WebApiClient.Create(request);\n\n    // Process response\n}\ncatch (error) {\n    // Handle error\n}\n```\n\n### Create\nThe client supports creation of records. You have to pass the entity logical name, and a data object:\n\n```JavaScript\nvar request = {\n    entityName: \"account\",\n    entity: {name: \"Adventure Works\"}\n};\n\nWebApiClient.Create(request)\n    .then(function(response){\n        // Process response\n    })\n    .catch(function(error) {\n        // Handle error\n    });\n```\n\n#### Return created record in create response\nThis feature is available from Dynamics365 v8.2 upwards.\nFor returning the full record that was created from your request, set an appropriate Prefer header as follows:\n\n```JavaScript\nvar request = {\n    entityName: \"account\",\n    entity: {name: \"Adventure Works\"},\n    headers: [{key: \"Prefer\", value: \"return=representation\"}]\n};\n\nWebApiClient.Create(request)\n    .then(function(response){\n        // Process response\n    })\n    .catch(function(error) {\n        // Handle error\n    });\n```\n\n### Retrieve\nThe client supports retrieving of records by Id, by alternate key, fetchXml and query expressions.\nFor retrieving by alternate key, pass an array of objects that each have a property and a value property.\nYou have to pass at least the entity logical name.\nYou can always pass query parameters which will be appended to your retrieve requests.\n\n#### Retrieve single records\n##### Retrieve by ID\n\n```JavaScript\nvar request = {\n    entityName: \"account\",\n    entityId: \"00000000-0000-0000-0000-000000000001\"\n};\n\nWebApiClient.Retrieve(request)\n    .then(function(response){\n        // Process response\n    })\n    .catch(function(error) {\n        // Handle error\n    });\n```\n\n##### Retrieve by alternate key\n\n```JavaScript\nvar request = {\n    entityName: \"contact\",\n    alternateKey:\n        [\n            { property: \"firstname\", value: \"Joe\" },\n            { property: \"emailaddress1\", value: \"abc@example.com\"}\n        ]\n};\n\nWebApiClient.Retrieve(request)\n    .then(function(response){\n        // Process response\n    })\n    .catch(function(error) {\n        // Handle error\n    });\n```\n\n#### Retrieve multiple records\nRetrieve of multiple records uses paging. Per default you can set a [page size on your requests](#page-size), however this is limited to 5000 records.\nIf you want to really retrieve all records, set WebApiClient.ReturnAllPages to true, as it is by default false, like this:\n\n``` JavaScript\nWebApiClient.ReturnAllPages = true;\n```\n\nBy setting this to true, each retrieve multiple request will check for an @odata.nextLink property inside the response, call the next page and concatenate the results, until all records have been retrieved.\n\nYou can also pass this option per-request, like this:\n\n```JavaScript\nvar request = {\n    entityName: \"account\",\n    queryParams: \"?$select=name,revenue,\u0026$orderby=revenue asc,name desc\u0026$filter=revenue ne null\",\n    returnAllPages: true\n};\n```\n\n##### Retrieve by query expression\n\n```JavaScript\nvar request = {\n    entityName: \"account\",\n    queryParams: \"?$select=name,revenue,\u0026$orderby=revenue asc,name desc\u0026$filter=revenue ne null\"\n};\n\nWebApiClient.Retrieve(request)\n    .then(function(response){\n        // Process response\n    })\n    .catch(function(error) {\n        // Handle error\n    });\n```\n\n##### Retrieve by FetchXml\nFetchXml requests have some special behaviour implemented. Short fetchXml will be sent as GET request using a fetchXml URL query parameter.\nThere is however an URL length limit of 2048 chars, so large fetchXml requests would fail, since they exceed this limit.\nSince release v3.1.0, the request will automatically be sent as POST batch request, so that large fetchXml can be executed as well.\nYou don't have to do anything for this to happen, the URL length is checked automatically before sending the request.\n\n```JavaScript\nvar request = {\n    entityName: \"account\",\n    fetchXml: \"\u003cfetch mapping='logical'\u003e\" +\n                \"\u003centity name='account'\u003e\" +\n                    \"\u003cattribute name='accountid'/\u003e\" +\n                    \"\u003cattribute name='name'/\u003e\" +\n                \"\u003c/entity\u003e\" +\n              \"\u003c/fetch\u003e\"\n};\n\nWebApiClient.Retrieve(request)\n    .then(function(response){\n        // Process response\n    })\n    .catch(function(error) {\n        // Handle error\n    });\n```\n\n#### Auto expand collection-valued navigation properties\nWhen retrieving collection-valued navigation properties, the expand is being deferred, i.e. you don't retrieve immediate results, but a property ending in \"@odata.nextLink\" that contains an URL to the results for this expand. You can read more about this [here](https://msdn.microsoft.com/en-us/library/gg334767.aspx#bkmk_expandRelated).\nFor easing to retrieve these, we can use the `WebApiClient.Expand` function. It takes an array of records and expands all properties, that end in \"@odata.nextLink\".\nYou can additionally pass headers to the request, that will be appended to each retrieve request for properties.\n\n```JavaScript\nWebApiClient.Retrieve({\n    entityName: \"account\",\n    queryParams: \"?$expand=contact_customer_accounts\"\n})\n.then(function(response){\n    return WebApiClient.Expand({\n        records: response.value\n    });\n})\n.then(function(response){        \n    // Process response\n})\n.catch(function(error) {\n    // Handle error\n});\n```\n\n### Update\nUpdate requests are supported. You have to pass the entity logical name, the ID of the record to update and an update object:\n\n```JavaScript\nvar request = {\n    entityName: \"account\",\n    entityId: \"00000000-0000-0000-0000-000000000001\",\n    entity: { name: \"Contoso\" }\n};\n\nWebApiClient.Update(request)\n    .then(function(response){\n        // Process response\n    })\n    .catch(function(error) {\n        // Handle error\n    });\n```\n\n#### Update by alternate key\n\n```JavaScript\nvar request = {\n    entityName: \"contact\",\n    alternateKey:\n        [\n            { property: \"firstname\", value: \"Joe\" },\n            { property: \"emailaddress1\", value: \"abc@example.com\"}\n        ],\n    entity: { lastname: \"Doe\" }\n};\n\nWebApiClient.Update(request)\n    .then(function(response){\n        // Process response\n    })\n    .catch(function(error) {\n        // Handle error\n    });\n```\n\n\u003e If you just issue update via alternate key without additional headers, it is always an upsert operation. If you only want to update records, you can send a header `If-Match: *`. To only create records, you can send a header `If-None-Match: *`. Read more about this in the [official documentation](https://learn.microsoft.com/en-us/power-apps/developer/data-platform/use-upsert-insert-update-record?tabs=webapi#using-web-api).\n\n#### Return updated record in update response\nThis feature is available from Dynamics365 v8.2 upwards.\nFor returning the full record after applying the updates from your request, set an appropriate Prefer header as follows:\n\n```JavaScript\nvar request = {\n    entityName: \"account\",\n    entityId: \"00000000-0000-0000-0000-000000000001\",\n    entity: { name: \"Contoso\" },\n    headers: [{key: \"Prefer\", value: \"return=representation\"}]\n};\n\nWebApiClient.Update(request)\n    .then(function(response){\n        // Process response\n    })\n    .catch(function(error) {\n        // Handle error\n    });\n```\n\n#### Clear Lookup value\nIf you're trying to clear a lookup value using an update with a null value, it might very well be, that it simply does nothing, or fails with an error such as `Property _pub_field_value cannot be updated to null. The reference property can only be deleted`.\nIn this case, you can not use an update request for clearing the lookup.\n\nTake a look at the [delete single property](#delete-single-property) section.\n\n### Delete\nDelete requests are supported. You have to pass the entity logical name, and ID of the record to delete:\n\n```JavaScript\nvar request = {\n    entityName: \"account\",\n    entityId: \"00000000-0000-0000-0000-000000000001\"\n};\n\nWebApiClient.Delete(request)\n    .then(function(response){\n        // Process response\n    })\n    .catch(function(error) {\n        // Handle error\n    });\n```\n\n#### Delete by alternate key\n\n```JavaScript\nvar request = {\n    entityName: \"contact\",\n    alternateKey:\n        [\n            { property: \"firstname\", value: \"Joe\" },\n            { property: \"emailaddress1\", value: \"abc@example.com\"}\n        ]\n};\n\nWebApiClient.Delete(request)\n    .then(function(response){\n        // Process response\n    })\n    .catch(function(error) {\n        // Handle error\n    });\n```\n\n#### Delete single property\nYou can delete single properties by passing the field to clear as queryParams with a preceding slash, like \"/telephone1\".\nIf it is a lookup, you'll have to prepend \"/$ref\", such as \"/primarycontactid/$ref\".\n\n```JavaScript\nvar request = {\n    entityName: \"account\",\n    entityId: \"00000000-0000-0000-0000-000000000001\",\n    queryParams: \"/primarycontactid/$ref\"\n};\n\nWebApiClient.Delete(request)\n    .then(function(response){\n        // Process response\n    })\n    .catch(function(error) {\n        // Handle error\n    });\n```\n\n### Associate\nAssociate requests are supported. You have to pass the relationship name, a source and a target entity.\nThis example associates an opportuntiy to an account:\n\n```JavaScript\nvar request = {\n    relationShip: \"opportunity_customer_accounts\",\n    source:\n        {\n            entityName: \"opportunity\",\n            entityId: \"00000000-0000-0000-0000-000000000001\"\n        },\n    target:\n        {\n            entityName: \"account\",\n            entityId: \"00000000-0000-0000-0000-000000000002\"\n        }\n};\n\nWebApiClient.Associate(request)\n    .then(function(response){\n        // Process response\n    })\n    .catch(function(error) {\n        // Handle error\n    });\n```\n\n### Disassociate\nDisassociate requests are supported. You have to pass the relationship name, a source and a target entity.\nThis example disassociates an opportuntiy from an account:\n\n```JavaScript\nvar request = {\n    relationShip: \"opportunity_customer_accounts\",\n    source:\n        {\n            entityName: \"opportunity\",\n            entityId: \"00000000-0000-0000-0000-000000000001\"\n        },\n    target:\n        {\n            entityName: \"account\",\n            entityId: \"00000000-0000-0000-0000-000000000002\"\n        }\n};\n\nWebApiClient.Disassociate(request)\n    .then(function(response){\n        // Process response\n    })\n    .catch(function(error) {\n        // Handle error\n    });\n```\n\n### Execute\nThere is support for executing actions / functions without having to use SendRequest.\nThe WebApiClient has a function WebApiClient.Execute, which takes a request as parameter.\nRequests are objects that base on the WebApiClient.Requests.Request base request.\nWhen wanting to send an already implemented request using Execute, you can either use the blank request (such as the WhoAmIRequest, that does not need any parameters), or in case it needs parameters, extend an existing request.\n\nMissing or custom action requests can be implemented as described [here](#not-yet-implemented-requests).\n\nCheck the [wiki](https://github.com/DigitalFlow/Xrm-WebApi-Client/wiki) for a list of requests that are implemented in the current release and examples on how to send them!\n\n#### No parameter request\nThe WhoAmI request does not need any parameters, therefore we can just pass the blank request:\n\n```JavaScript\nvar request = WebApiClient.Requests.WhoAmIRequest;\n\nWebApiClient.Execute(request)\n    .then(function(response){\n        // Process response\n    })\n    .catch(function(error) {\n        // Handle error\n    });\n```\n\n#### Parametrized request\nMost requests need further parameters for being sent.\nWhen needing to send those requests, start with the blank request and call the function \"with\" on it, passing the needed parameters as object to it. Your passed-in parameters override possibly existing parameters with the same name.\n\nThe following parameters are supported:\n- method - HTTP method for request (Required, but defined by request)\n- name - Name of the request as used for the URL (Required, but defined by request)\n- bound - Pass true if request is bound to a record, false if not. Has consequences for automatic URL building. By default false and defined by request.\n- entityName - Name of the request's target entity. Defined by request if always the same.\n- entityId - ID of the request's target record\n- payload - Object that is sent as payload for the request\n- headers - Headers that should be set on the request\n- urlParams - Any parameters that have to be embedded in the request URL, as described [here](https://msdn.microsoft.com/en-us/library/gg309638.aspx#Anchor_2). Pass an object with parameter names as keys and the corresponding values.\n\nSample request for AddToQueue:\n```JavaScript\nvar request = WebApiClient.Requests.AddToQueueRequest\n    .with({\n        entityId: \"56ae8258-4878-e511-80d4-00155d2a68d1\",\n        payload: {\n            Target: {\n                activityid: \"59ae8258-4878-e511-80d4-00155d2a68d1\",\n                \"@odata.type\": \"Microsoft.Dynamics.CRM.letter\"\n            }\n        }\n    });\n\nWebApiClient.Execute(request)\n    .then(function(response){\n        // Process response\n    })\n    .catch(function(error) {\n        // Handle error\n    });\n```\n\n#### Component Types\nSometimes you need to pass a \"Component Type\" parameter, especially with metadata requests.\nIf you don't know the proper value, use this request to list all the available Component Types and their values in your org:\n\n```JavaScript\nawait WebApiClient.Retrieve({ overriddenSetName: \"GlobalOptionSetDefinitions(Name='componenttype')\" })\n```\n\n### Send Batch\nThere is support for sending multiple requests as a batch. Batch requests can contain retrieve requests and change sets. Change sets can contain requests themselves, however they must not contain other change sets.\n\nRequests directly attached to batch requests have to be GET requests, they must not be added to change sets, since those have to contain requests, that change data.\nBatch requests provide transactional functionality, so all operations contained in one change set will roll back, if any of them fails.\nYou can read more about batch requests in general [here](https://msdn.microsoft.com/en-us/library/mt607719.aspx).\nThere is also a useful [OData Reference](http://www.odata.org/documentation/odata-version-3-0/batch-processing/) covering this topic (albeit v3.0).\n\n#### How to create batch requests\nFor creating requests for usage inside batches, you can either create a `WebApiClient.BatchRequest` object using its constructor, or easier, call one of the WebApiClient functions and pass `asBatch: true` as parameter.\n\nAll functions, such as CRUD, Execute and so on support this parameter. The only exception to it is the Expand function.\n\nBelow is an example for creating two tasks attached to an account in one change set, while returning the records created.\nAfterwards, the account they were attached to is returned:\n\n```JavaScript\nWebApiClient.Create({entityName: \"account\", entity: { name: \"Test\" }})\n    .then(function (account) {\n        var accountId = account.substring(account.indexOf(\"(\")).replace(\"(\", \"\").replace(\")\",\"\");\n\n        var batch = new WebApiClient.Batch({\n            changeSets: [\n                new WebApiClient.ChangeSet({\n                    requests: [\n                        WebApiClient.Create({\n                           entityName: \"task\",\n                           entity: {\n                             subject: \"Task 1 in batch\",\n                             \"regardingobjectid_account_task@odata.bind\": \"/accounts(\" + accountId + \")\"\n                           },\n                           headers: [{key: \"Prefer\", value:\"return=representation\"}],\n                           asBatch: true\n                        }),\n                        WebApiClient.Create({\n                           entityName: \"task\",\n                           entity: {\n                             subject: \"Task 2 in batch\",\n                             \"regardingobjectid_account_task@odata.bind\": \"/accounts(\" + accountId + \")\"\n                           },\n                           headers: [{key: \"Prefer\", value:\"return=representation\"}],\n                           asBatch: true\n                        })\n                 ]\n             })],\n            requests: [\n                WebApiClient.Retrieve({\n                    entityName: \"account\",\n                    entityId: accountId,\n                    asBatch: true\n                })\n            ]\n        });\n        \n        return WebApiClient.SendBatch(batch);\n    })\n    .then(function(result) {\n        if(result.isFaulted) {\n            console.log(result.errors);\n        }\n        \n        // Logs BatchResponse with name, batchResponses array and changeSetResponses array\n        console.log(result);\n    })\n    .catch(function(error) {\n        // Handle network error or similar\n    });\n```\nNote: Above code is only an example, you could also create your batch and change sets separately and add the change sets to `batch.changeSets`, requests inside the change sets to `changeSet.requests` and plain batch requests to `batch.requests`, which are all arrays.\n\n#### Batch Responses\nCalls to `WebApiClient.SendBatch` will return a BatchResponse.\nA batch response consist of an array `batchResponses`, that contains all responses for GET requests, that were directly attached to the batch and an array `changeSetResponses`, that contains one change set response for each change set that was sent.\nEach change set response contains its own `responses` array, with responses for each request inside the changset.\n\nAt the top level, each batch response also has a `name`, an `isFaulted` property that evaluates to true, if any of the requests failed and an `errors` array that contains all responses for failed requests.\n\nThe lowest level responses all contain a `headers` object, so you can access headers easier (e.g. headers[\"OData-EntityId\"]), a `payload` object, a `status` (such as \"200\") and a `contentId`, if the requests inside the change set had one set.\n\nScheme of a batch response:\n- batchResponses (array)\n    - Response (WebApiClient.Response)\n        - contentId (string)\n        - headers (array of string * string)\n        - payload (object)\n        - status (string)\n    - ...\n- changeSetResponses (array)\n    - changeSetResponse (object)\n        - name (string)\n        - responses (array)\n            - Response (WebApiClient.Response)\n                - contentId (string)\n                - headers (array of string * string)\n                - payload (object)\n                - status (string)\n            - ...\n- errors (array of WebApiClient.Response)\n- isFaulted (bool)\n- name (string)\n  \n\n#### Request failures\nIf a request inside the batch requests or a change set fails, the batch response property `isFaulted` will have the value `true`.\nYou can get a collection of all errors using the response property `errors`.\n\nThis is all inside the `then` handler, remember that you should still configure a `catch` handler, as this will be needed if a requests fails due to network errors or similar.\n\n### Configuration\nWhen having to set multiple configuration settings for the WebApiClient, you can use the ```Configure``` function, which gets an object passed with keys and values, that get projected onto the WebApiClient:\n\n```JavaScript\nWebApiClient.Configure({\n    ApiVersion: \"8.2\",\n    ReturnAllPages: true,\n    PrettifyErrors: false\n});\n```\n\n### Errors\nIf errors occur during processing of requests, the WebAPI client by default throws an error with the text that follows this format: xhr.statusText: xhr.response.message, i.e. \"Internal Server Error: The function parameter 'EntityMoniker' cannot be found.Parameter name: parameterName\".\n\nFor returning the whole stringified JSON response including a custom xhrStatusText property, set\n\n```JavaScript\nWebApiClient.PrettifyErrors = false;\n```\n\n### Set Names\nSet names are automatically generated according to WebApi rules and based on the entityName parameter in your request.\nHowever there are some set names, that are not generated according to naming rules, for example ContactLeads becomes contactleadscollection. For handling those corner cases, each request allows to pass an overriddenSetName instead of the entity name, so that you can directly pass those set names that break naming rules. This should happen very rarely.\nExample of passing overriddenSetName:\n\n```JavaScript\nvar request = {\n    overriddenSetName: \"contactleadscollection\",\n    entity: {name: \"Contoso\"}\n};\n\nWebApiClient.Create(request)\n    .then(function(response){\n        // Process response\n    })\n    .catch(function(error) {\n        // Handle error\n    });\n```\n\n### Not yet implemented requests\nIf you need to use requests, that are not yet implemented (such as custom actions), you can create an executor for the missing request and append it to the WebApiClient.Requests object (if you want to reuse it). Be sure to create your missing request by calling Object.create on the base request object.\nThis might look something like this:\n\n```JavaScript\nWebApiClient.Requests.AddToQueueRequest = WebApiClient.Requests.Request.prototype.with({\n    method: \"POST\",\n    name: \"AddToQueue\",\n    bound: true,\n    entityName: \"queue\"\n});\n\n```\nFor further explanations regarding these requests, please check [here](#execute).\nAll requests should be implemented basically by now, in case of any errors in the implementations, you can override any property using the ```with``` function as described [here](#execute).\n\nAlternatively, you can use the ```WebApiClient.SendRequest``` function.\nIn combination with ```WebApiClient.GetApiUrl``` and ```WebApiClient.GetSetName``` you can easily build up your request url, set your HTTP method and attach additional payload or headers.\n\nAn example of a custom implementation of the WinOpportunity request:\n```Javascript\nvar url = WebApiClient.GetApiUrl() + \"WinOpportunity\";\nvar opportunityId = \"00000000-0000-0000-0000-000000000001\";\nvar payload = {\n    \"Status\": 3,\n    \"OpportunityClose\": {\n        \"subject\": \"Won Opportunity\",\n        \"opportunityid@odata.bind\": \"/\" + WebApiClient.GetSetName(\"opportunity\") + \"(\" + opportunityId + \")\"\n    }\n};\n\nWebApiClient.SendRequest(\"POST\", url, payload)\n    .then(function(response){\n        // Process response\n    })\n    .catch(function(error) {\n        // Handle error\n    });\n```\n\n### Promises\nThis client uses bluebird internally for handling promises in a cross-browser compliant way.\nTherefore the promises returned by all asynchronous requests are also bluebird promises.\nBluebird itself is not exported globally anymore as of v3.0.0, but can be accessed by using ```WebApiClient.Promise```.\nThis decision was made for not causing issues with other scripts.\n\nUsing promises you can do something like this, too:\n```Javascript\nvar requests = [];\n\nfor (var i = 0; i \u003c 5; i++) {\n    var request = {\n        entityName: \"account\",\n        entity: {name: \"Adventure Works Nr. \" + i}\n    };\n    requests.push(WebApiClient.Create(request));\n}\n\nWebApiClient.Promise.all(requests)\n    .then(function(response){\n        // Process response\n    })\n    .catch(function(error) {\n        // Handle error\n    });\n```\n\n## External Access\nExternal access to CRM, that is accessing CRM without being on a CRM form or having a ClientGlobalContext.aspx is supported.\nAs OAuth is required for authenticating, only CRM online and CRM On-Premises with IFD and Azure AD are supported.\n\nYou'll have to register the WebApiClient as App in Azure AD, which is described in [MSDN](https://msdn.microsoft.com/en-us/library/mt595797.aspx).\n\n### Single Page Application\nThere is support for using the client inside external single page applications.\n\nThere is a minimal working example in the [sample folder](https://github.com/DigitalFlow/Xrm-WebApi-Client/blob/master/src/sample/SinglePageApp.html).\n\n## Headers\nThere is a defined set of default headers, which are sent on each request, as well as per-request headers.\nPer-request headers override possibly existing default headers with the same key value.\n\n### Header Format\nHeaders are represented as objects containing a key and a value property:\n\n```JavaScript\nvar header = { key: \"headerKey\", value: \"headerValue\" };\n```\n\n### Default Headers\nBy default there is a defined set of default headers, that will be sent with each request.\nThe default headers can be retrieved using the WebApiClient.GetDefaultHeaders function.\nYou can however add own default headers by using the WebApiClient.AppendToDefaultHeaders function, which takes as much headers as dynamic arguments as you like.\n\nExample:\n```JavaScript\nvar header = {key: \"newHeader\", value: \"newValue\"};\nWebApiClient.AppendToDefaultHeaders (header);\n```\n\n### Request Headers\nYou can also attach headers per request, all request parameters have a headers property, that can be used for passing per-request headers.\n\nThis could look something like this:\n``` JavaScript\n// Parameters for create request\nvar request = {\n    entityName: \"account\",\n    entity: {name: \"Adventure Works\"},\n    headers: [ { key: \"headerKey\", value: \"headerValue\" }]\n};\n```\n\n#### Page size\nIf you want to set a max page size for your request (supported are up to 5000 records per page), you can pass the following header:\n\n``` JavaScript\nheaders: [ { key: \"Prefer\", value: \"odata.maxpagesize=5000\" }]\n```\n\n### API Version\nThe default API version is 8.0.\nYou can however change it to 8.1 if needed by using\n\n```JavaScript\nWebApiClient.ApiVersion = \"8.1\";\n```\n\n## Remarks\n### CRM App\nFor using WebApiClient with the CRM App, you'll have to use the normal (= not uglified) version.\nWhen using uglified JS in the CRM App, you might receive invalid character errors. This is not only valid for the WebApiClient, but also for some other uglified code.\n\n## FAQ\n### Payloads\nWhen sending data for entity records, there often arise questions, on how to pass specific values.\nSome attributes can be directly set, some need a special format for the Web API to recognize them.\n\nSimple Attributes (Just use native values in payload):\n- Single Line of Text\n- Multiple Lines of Text\n- Number\n- Decimal\n- Float\n- Boolean\n- OptionSet (Just use the option set value)\n- DateTime\n\nComplex Attributes:\n- Lookups: When setting lookups, you use the attribute logical name, followed by \"@odata.bind\". As value, you need to pass a special format of the id, which is \"/entityListName(id)\".\nSo for setting the parent account of an account, the payload would look like this:\n\n```JavaScript\nvar update = {\n \"parentaccountid@odata.bind\": \"/accounts(4acc8857-fbb8-42d1-a5c5-24d83c9d1380)\"\n};\n```\nFor getting the entity list name, you can use `WebApiClient.GetSetName`.\n\nSpecial case: \nA special case are lookups, which target multiple entities, such as customer lookups or regarding object ids.\nThose apply the same rules as plain lookups, but in addition to that, the entity logical name of the target related entity is appended to the field name, preceeded by an underscore.\nFor example to set the regardingobject of an appointment to our previous parent account, we would have to use the following payload:\n\n```JavaScript\nvar update = {\n \"regardingobjectid_account@odata.bind\": \"/accounts(4acc8857-fbb8-42d1-a5c5-24d83c9d1380)\"\n};\n```\n\n### Logical Names\nSometimes requests fail, due to the Web API not finding the attributes you included in your payload in the entity definition.\nMost often this is because you got the name wrong.\nFor finding the proper names, you can head to Settings \u003e Customizations \u003e Developer Resources and click on the \"Download OData Metadata\" link. You will be provided with a XML file, which contains all entities that are exposed in the Web API, with their set names and all of their attributes.\n\n### Multiselect OptionSets\nMulti Select option sets are typed as Edm.String instead of Edm.Int.\nYou should not use \"eq\" for filtering on them, as this would fail if multiple options are selected.\n\nFiltering on multi select option sets works as follows: \n`?$filter=Microsoft.Dynamics.CRM.ContainValues(PropertyName='oss_multiselect',PropertyValues=['0','1'])`\n\nor in fetchXML:\n```XML\n\u003cfilter\u003e\n  \u003ccondition attribute=\"oss_multiselect\" condition=\"contain-values\"\u003e\n    \u003cvalue\u003e0\u003c/value\u003e\n    \u003cvalue\u003e1\u003c/value\u003e\n  \u003c/condition\u003e\n\u003c/filter\u003e\n```\n\nthere's similarly `not-contain-values` as well:\n`?$filter=Microsoft.Dynamics.CRM.DoesNotContainValues(PropertyName='oss_multiselect',PropertyValues=['0','1'])`\n\nor in fetchXML:\n```XML\n\u003cfilter\u003e\n  \u003ccondition attribute=\"oss_multiselect\" condition=\"not-contain-values\"\u003e\n    \u003cvalue\u003e0\u003c/value\u003e\n    \u003cvalue\u003e1\u003c/value\u003e\n  \u003c/condition\u003e\n\u003c/filter\u003e\n```\n\n### Alternate Key with Lookup property\n\nIf you want to use alternate keys which contain lookup properties, make sure you use the _${logicalName}_value syntax for referencing the field.\nIt can look something like this:\n\n```JavaScript\nvar request = {\n    entityName: \"oss_customentity\",\n    alternateKey:\n        [\n            { property: \"_oss_contactid_value\", value: \"30cc3c62-87cf-ed11-b597-000d3ab693ef\" }\n        ],\n    entity: {  }\n};\n\nawait WebApiClient.Update(request);\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fxrm-oss%2Fxrm-webapi-client","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fxrm-oss%2Fxrm-webapi-client","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fxrm-oss%2Fxrm-webapi-client/lists"}