Update a Microsoft Graph credential
Update a Microsoft Graph app-only credential. You can rename the credential, or rotate its client secret by sending both client_secret and secret_expires_at in the same request.
Request
Endpoint
PATCH https://api.woodpecker.co/rest/v2/mailboxes/microsoft/credentials/{credential_id}
Headers
x-api-key: {YOUR_API_KEY}
Content-Type: application/json
For details on how to authenticate your requests, please see the authentication guide.
Body
{
"client_secret": "new-your-client-secret",
"secret_expires_at": "2027-11-28T23:59:59Z",
"name": "MS Graph app - production"
}
Body schema
| Field | Type | Required | Description |
|---|---|---|---|
client_secret | string | No* | New client secret for the Microsoft app registration |
secret_expires_at | string | No* | Client secret expiration date in ISO 8601 format |
name | string | No | Credential name |
*If you send either client_secret or secret_expires_at, both fields are required.
Request samples
Rotate a credential secret
- cURL
- Python
- Java
- Node.js
- PHP
curl --request PATCH \
--url "https://api.woodpecker.co/rest/v2/mailboxes/microsoft/credentials/{credential_id}" \
--header "x-api-key: {YOUR_API_KEY}" \
--header "Content-Type: application/json" \
--data '{
"client_secret": "new-your-client-secret",
"secret_expires_at": "2027-11-28T23:59:59Z",
"name": "MS Graph app - production"
}'
import requests
def update_microsoft_graph_credential(credential_id):
url = f"https://api.woodpecker.co/rest/v2/mailboxes/microsoft/credentials/{credential_id}"
headers = {
"x-api-key": "{YOUR_API_KEY}",
"Content-Type": "application/json",
}
payload = {
"client_secret": "new-your-client-secret",
"secret_expires_at": "2027-11-28T23:59:59Z",
"name": "MS Graph app - production",
}
response = requests.patch(url, headers=headers, json=payload)
if response.status_code == 200:
print(response.json())
else:
print("Request failed:", response.status_code, response.text)
if __name__ == "__main__":
update_microsoft_graph_credential(22334455)
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
public class WoodpeckerApiClient {
private static final String API_KEY = "{YOUR_API_KEY}";
public static void main(String[] args) {
updateMicrosoftGraphCredential(22334455);
}
public static void updateMicrosoftGraphCredential(int credentialId) {
try {
String url = "https://api.woodpecker.co/rest/v2/mailboxes/microsoft/credentials/" + credentialId;
String jsonData = """
{
"client_secret": "new-your-client-secret",
"secret_expires_at": "2027-11-28T23:59:59Z",
"name": "MS Graph app - production"
}
""";
HttpClient client = HttpClient.newHttpClient();
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create(url))
.header("x-api-key", API_KEY)
.header("Content-Type", "application/json")
.method("PATCH", HttpRequest.BodyPublishers.ofString(jsonData))
.build();
HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());
if (response.statusCode() == 200) {
System.out.println(response.body());
} else {
System.err.println("Request failed: " + response.statusCode());
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
const axios = require("axios");
async function updateMicrosoftGraphCredential(credentialId) {
const url = `https://api.woodpecker.co/rest/v2/mailboxes/microsoft/credentials/${credentialId}`;
const headers = {
"x-api-key": "{YOUR_API_KEY}",
"Content-Type": "application/json"
};
const data = {
client_secret: "new-your-client-secret",
secret_expires_at: "2027-11-28T23:59:59Z",
name: "MS Graph app - production"
};
try {
const response = await axios.patch(url, data, { headers });
if (response.status === 200) {
console.log(response.data);
} else {
console.error("Request failed:", response.status);
}
} catch (error) {
console.error("Request error:", error.response?.status || error.message);
}
}
updateMicrosoftGraphCredential(22334455);
<?php
require 'vendor/autoload.php';
use GuzzleHttp\Client;
use GuzzleHttp\Exception\RequestException;
$credentialId = 22334455;
$client = new Client([
'base_uri' => 'https://api.woodpecker.co/rest/v2/',
'headers' => [
'x-api-key' => getenv('WOODPECKER_API_KEY'),
'Content-Type' => 'application/json',
],
]);
try {
$response = $client->patch("mailboxes/microsoft/credentials/{$credentialId}", [
'json' => [
'client_secret' => 'new-your-client-secret',
'secret_expires_at' => '2027-11-28T23:59:59Z',
'name' => 'MS Graph app - production',
],
]);
echo $response->getStatusCode(), "\n";
echo $response->getBody(), "\n";
} catch (RequestException $e) {
echo "Error: ", $e->getMessage(), "\n";
if ($e->hasResponse()) {
echo $e->getResponse()->getBody(), "\n";
}
}
Response
Response examples
- 200
- 401
- 404
- 422
- 500
The credential was updated.
{
"credential_id": 22334455,
"tenant_id": "bbfb2db7-aaf4-4f7c-9dc2-2c9160d1cb32",
"client_id": "e1c6ac69-480e-4a17-9b85-3467967bd179",
"secret_expires_at": "2027-11-28T23:59:59Z",
"name": "MS Graph app - production"
}
Body schema
| Field | Type | Description |
|---|---|---|
credential_id | integer | Updated credential ID |
tenant_id | string | Microsoft Entra tenant ID |
client_id | string | Microsoft Entra application client ID |
secret_expires_at | string | Client secret expiration date in ISO 8601 format |
name | string | Credential name |
An issue with authorization. Please review the authorization guide.
{
"title": "Unauthorized",
"status": 401,
"detail": "Invalid api key",
"timestamp": "2025-03-05 17:57:00"
}
Body schema
| Field | Type | Description |
|---|---|---|
title | string | A short title describing the error |
status | integer | The HTTP status code |
detail | string | A detailed message explaining the error |
timestamp | string | The timestamp when the error occurred, YYYY-MM-DD HH:MM:SS UTC |
Please review the request URL.
{
"title": "Not Found",
"status": 404,
"detail": "Requested resource does not exist",
"timestamp": "2025-03-05 17:57:00"
}
Body schema
| Field | Type | Description |
|---|---|---|
title | string | A short title describing the error |
status | integer | The HTTP status code |
detail | string | A detailed message explaining the error |
timestamp | string | The timestamp when the error occurred, YYYY-MM-DD HH:MM:SS UTC |
Invalid request or malformed request syntax. The request can also fail when the body is valid JSON, but the credential cannot be updated.
{
"message": "CLIENT_SECRET_AND_EXPIRY_MUST_BE_PROVIDED_TOGETHER"
}
Body schema
| Field | Type | Description |
|---|---|---|
message | string | Validation error message. Possible values:
|
Unexpected error, please try again later
{
"title": "Internal server error",
"status": 500,
"detail": "An unexpected error has occurred. Please try again later.",
"timestamp": "2025-03-05 17:57:00"
}
Body schema
| Field | Type | Description |
|---|---|---|
title | string | A short title describing the error |
status | integer | The HTTP status code |
detail | string | A detailed message explaining the error |
timestamp | string | The timestamp when the error occurred, YYYY-MM-DD HH:MM:SS UTC |