diff --git a/AzureSearchEmulator.IntegrationTests/DocumentIndexRouteIntegrationTests.cs b/AzureSearchEmulator.IntegrationTests/DocumentIndexRouteIntegrationTests.cs
new file mode 100644
index 0000000..3989697
--- /dev/null
+++ b/AzureSearchEmulator.IntegrationTests/DocumentIndexRouteIntegrationTests.cs
@@ -0,0 +1,55 @@
+using System.Net;
+using System.Text;
+using Azure.Search.Documents.Indexes.Models;
+using Xunit;
+
+namespace AzureSearchEmulator.IntegrationTests;
+
+///
+/// Azure accepts a document batch on both docs/search.index and docs/index; the
+/// Go SDK posts to the latter. Both spellings, in both the path and OData-key forms, must reach
+/// the same handler.
+///
+public class DocumentIndexRouteIntegrationTests(EmulatorFactory factory)
+ : IClassFixture
+{
+ [Theory]
+ [InlineData("indexes/{0}/docs/index")]
+ [InlineData("indexes('{0}')/docs/index")]
+ [InlineData("indexes/{0}/docs/search.index")]
+ public async Task IndexDocuments_AcceptsBatchOnEitherRoute(string routeFormat)
+ {
+ var indexName = "docs-index-route-" + Guid.NewGuid().ToString("N")[..8];
+ var cancellationToken = TestContext.Current.CancellationToken;
+
+ var indexClient = factory.CreateSearchIndexClient();
+ await indexClient.CreateIndexAsync(
+ new SearchIndex(indexName)
+ {
+ Fields =
+ {
+ new SearchField("id", SearchFieldDataType.String) { IsKey = true },
+ new SearchField("name", SearchFieldDataType.String) { IsSearchable = true },
+ }
+ },
+ cancellationToken);
+
+ using var http = factory.CreateHttpClient();
+
+ var response = await http.PostAsync(
+ string.Format(routeFormat, indexName) + "?api-version=2024-07-01",
+ new StringContent(
+ """{ "value": [ { "@search.action": "upload", "id": "1", "name": "hello" } ] }""",
+ Encoding.UTF8,
+ "application/json"),
+ cancellationToken);
+
+ Assert.Equal(HttpStatusCode.OK, response.StatusCode);
+
+ var searchClient = factory.CreateSearchClient(indexName);
+ var document = await searchClient.GetDocumentAsync>("1", cancellationToken: cancellationToken);
+ Assert.Equal("hello", document.Value["name"].ToString());
+
+ await indexClient.DeleteIndexAsync(indexName, cancellationToken);
+ }
+}
diff --git a/AzureSearchEmulator/Controllers/DocumentIndexingController.cs b/AzureSearchEmulator/Controllers/DocumentIndexingController.cs
index 8faa27a..c54ee8b 100644
--- a/AzureSearchEmulator/Controllers/DocumentIndexingController.cs
+++ b/AzureSearchEmulator/Controllers/DocumentIndexingController.cs
@@ -22,9 +22,15 @@ public class DocumentIndexingController(
///
private const int MaxLoggedBodyLength = 4096;
+ ///
+ /// Azure accepts both docs/search.index and the shorter docs/index for a batch;
+ /// the Go and Java SDKs post to the latter.
+ ///
[HttpPost]
[Route("indexes({indexKey})/docs/search.index")]
[Route("indexes/{indexKey}/docs/search.index")]
+ [Route("indexes({indexKey})/docs/index")]
+ [Route("indexes/{indexKey}/docs/index")]
public async Task IndexDocuments(string indexKey)
{
// Strip quotes that may be captured from OData-style URLs