create-user-notification
dci create-user-notification [body] [flags]
Creates a personal cost-anomaly notification config for the user. The request body has no email address; the server copies the target user's email onto the config as the sole destination.
Requires usersManager permission. The user id must belong to the resolved tenant.
Requires the Idempotency-Key header so a retried create with the same key and body returns the
original response and does not create a second config. With dryRun=true the request goes through
the same checks as a real create and returns 200 with X-Dry-Run: true and null identity fields
(id, createTime, updateTime, etag) without creating anything.
Reads are immediately consistent. After a 201, a GET of that notification returns the created
config.
Pass the request body as name: value arguments or pipe JSON on stdin — see Command structure.
Request
Content-Type: application/json
| Field | Type | Required | Description |
|---|---|---|---|
name | string | yes | Display name for the new notification config. |
settings | object | yes | Subscribed notification settings. Only costAnomalies is accepted. Settings for any other type are rejected; omitting costAnomalies is rejected. |
settings.costAnomalies | object | yes | Cost-anomaly notification settings: minimum severity, excess-cost threshold, allocation scope, and optional resend controls. |
settings.costAnomalies.minSeverity | string | yes | warning — warning and above. critical — critical only. There is no information or all value; a write of all is rejected with 422. A read of a missing stored severity (console "All severity levels") and of warning both return warning. critical is stored and returned as critical. One of: "warning", "critical". |
settings.costAnomalies.minExcessCost | object | yes | Minimum excess cost, as Money. Notify when excess cost is above this amount, not total spend. 100.00 USD means $100. currency must be USD; any other currency is rejected with 422. A negative amount is rejected with 422. A fractional amount is rejected with 422 (100.99 is not a whole number of dollars). 100 and 100.00 are both accepted. Amounts above 1,000,000,000 whole dollars, and forms such as Inf, exponents, a leading +, or underscores, are rejected with 422. Responses return the amount as "100.00". 0 is accepted. |
settings.costAnomalies.minExcessCost.amount | string | yes | Decimal monetary amount at ISO 4217 minor-unit precision (string). |
settings.costAnomalies.minExcessCost.currency | string | yes | ISO 4217 currency code. |
settings.costAnomalies.allocations | object | yes | Allocation scope for cost-anomaly notifications. ids are allocation ids from GET /analytics/v1/allocations. An id outside the caller's tenant is rejected with 422 without saying whether it exists elsewhere. mode: all means every allocation with anomaly detection enabled, including anomalies that are not tied to an allocation, and it carries an empty ids. include and exclude require at least one id. ids are an ordered list: the server stores and returns them in the order sent, and it does not sort or drop entries. A duplicate id is rejected with 422. |
settings.costAnomalies.allocations.mode | string | yes | all — every allocation with anomaly detection enabled (and unallocated anomalies). include — only the listed ids. exclude — all enabled allocations except the listed ids. One of: "all", "include", "exclude". |
settings.costAnomalies.allocations.ids | array of string | yes | Allocation ids, in the order sent. A read returns that same order. A duplicate id is rejected with 422. Empty when mode is all. Non-empty when mode is include or exclude. |
settings.costAnomalies.repeat | object | Resend controls for an active cost anomaly. Omit the whole repeat object when neither control is set. An empty object is rejected with 422. | |
settings.costAnomalies.repeat.everyHours | integer | Resend until the anomaly is inactive, every N hours (1–24). | |
settings.costAnomalies.repeat.whenCostGrows | object | Resend when the anomaly's cost grows: - both percentIncrease and minIncrease present → match is required (or or and) - exactly one of percentIncrease or minIncrease present → omit match (percentage-only or value-only) - neither amount present → rejected with 422 percentIncrease is an integer percent (10 means 10 percent). Omit withinHours when the time window is off. Nested fields omitted — see the raw JSON schema. |
Example body (JSON)
{
"name": "Anomaly alerts",
"settings": {
"costAnomalies": {
"minSeverity": "warning",
"minExcessCost": {
"amount": "100.00",
"currency": "USD"
},
"allocations": {
"mode": "all",
"ids": []
}
}
}
}
Raw JSON schema
{
"type": "object",
"additionalProperties": false,
"description": "Fields of a new personal notification. No email address — the server copies the target user's email\nas the destination. Unknown fields are rejected. `settings` must include `costAnomalies` and must\nnot include any other notification type.\n",
"required": [
"name",
"settings"
],
"properties": {
"name": {
"type": "string",
"minLength": 1,
"maxLength": 200,
"description": "Display name for the new notification config.",
"example": "Anomaly alerts"
},
"settings": {
"type": "object",
"additionalProperties": false,
"description": "Subscribed notification settings. Only `costAnomalies` is accepted. Settings for any other type are\nrejected; omitting `costAnomalies` is rejected.\n",
"required": [
"costAnomalies"
],
"properties": {
"costAnomalies": {
"type": "object",
"additionalProperties": false,
"description": "Cost-anomaly notification settings: minimum severity, excess-cost threshold, allocation scope, and\noptional resend controls.\n",
"required": [
"minSeverity",
"minExcessCost",
"allocations"
],
"properties": {
"minSeverity": {
"type": "string",
"enum": [
"warning",
"critical"
],
"description": "`warning` — warning and above. `critical` — critical only. There is no `information` or `all`\nvalue; a write of `all` is rejected with `422`. A read of a missing stored severity (console \"All\nseverity levels\") and of warning both return `warning`. `critical` is stored and returned as\n`critical`.\n",
"example": "warning"
},
"minExcessCost": {
"description": "Minimum excess cost, as Money. Notify when excess cost is above this amount, not total spend.\n`100.00` USD means $100. `currency` must be `USD`; any other currency is rejected with `422`. A\nnegative amount is rejected with `422`. A fractional amount is rejected with `422` (`100.99` is not a\nwhole number of dollars). `100` and `100.00` are both accepted. Amounts above 1,000,000,000 whole\ndollars, and forms such as `Inf`, exponents, a leading `+`, or underscores, are rejected with `422`.\nResponses return the amount as `\"100.00\"`. `0` is accepted.\n",
"allOf": [
{
"type": "object",
"required": [
"amount",
"currency"
],
"properties": {
"amount": {
"type": "string",
"pattern": "^-?(0|[1-9]\\d*)(\\.\\d+)?$",
"description": "Decimal monetary amount at ISO 4217 minor-unit precision (string).",
"example": "100.00"
},
"currency": {
"type": "string",
"pattern": "^[A-Z]{3}$",
"description": "ISO 4217 currency code.",
"example": "USD"
}
}
}
]
},
"allocations": {
"type": "object",
"additionalProperties": false,
"description": "Allocation scope for cost-anomaly notifications. `ids` are allocation ids from `GET\n/analytics/v1/allocations`. An id outside the caller's tenant is rejected with `422` without saying\nwhether it exists elsewhere.\n\n`mode: all` means every allocation with anomaly detection enabled, including anomalies that are not\ntied to an allocation, and it carries an empty `ids`. `include` and `exclude` require at least one\nid. `ids` are an ordered list: the server stores and returns them in the order sent, and it does not\nsort or drop entries. A duplicate id is rejected with `422`.\n",
"required": [
"mode",
"ids"
],
"properties": {
"mode": {
"type": "string",
"enum": [
"all",
"include",
"exclude"
],
"description": "`all` — every allocation with anomaly detection enabled (and unallocated anomalies). `include` —\nonly the listed ids. `exclude` — all enabled allocations except the listed ids.\n",
"example": "include"
},
"ids": {
"type": "array",
"description": "Allocation ids, in the order sent. A read returns that same order. A duplicate id is rejected\nwith `422`. Empty when `mode` is `all`. Non-empty when `mode` is `include` or `exclude`.\n",
"items": {
"type": "string",
"minLength": 1
},
"example": [
"alloc_abc",
"alloc_def"
]
}
}
},
"repeat": {
"type": "object",
"additionalProperties": false,
"minProperties": 1,
"description": "Resend controls for an active cost anomaly. Omit the whole `repeat` object when neither control is\nset. An empty object is rejected with `422`.\n",
"properties": {
"everyHours": {
"type": "integer",
"minimum": 1,
"maximum": 24,
"description": "Resend until the anomaly is inactive, every N hours (1–24).\n",
"example": 24
},
"whenCostGrows": {
"type": "object",
"additionalProperties": false,
"minProperties": 1,
"description": "Resend when the anomaly's cost grows:\n\n- both `percentIncrease` and `minIncrease` present → `match` is\n required (`or` or `and`)\n- exactly one of `percentIncrease` or `minIncrease` present → omit\n `match` (percentage-only or value-only)\n- neither amount present → rejected with `422`\n\n`percentIncrease` is an integer percent (`10` means 10 percent). Omit `withinHours` when the time\nwindow is off.\n",
"properties": {
"match": {
"type": "string",
"enum": [
"or",
"and"
],
"description": "How to combine percent and dollar growth when both are set. Required when both amounts are present;\nomit when only one amount is present.\n",
"example": "or"
},
"percentIncrease": {
"type": "integer",
"minimum": 1,
"description": "Minimum percent cost increase that triggers a resend. `10` means 10 percent. Must be greater than 0.\n",
"example": 10
},
"minIncrease": {
"description": "Minimum cost growth that triggers a resend, as Money. Must be a positive whole number of dollars.\n`150.00` USD means $150. `currency` must be `USD`; any other currency is rejected with `422`. A\nnegative amount is rejected with `422`. A fractional amount is rejected with `422` (`150.99` is not a\nwhole number of dollars). `150` and `150.00` are both accepted. Amounts above 1,000,000,000 whole\ndollars, and forms such as `Inf`, exponents, a leading `+`, or underscores, are rejected with `422`.\nResponses return the amount as `\"150.00\"`.\n",
"allOf": [
{
"type": "object",
"required": [
"amount",
"currency"
],
"properties": {
"amount": {
"type": "string",
"pattern": "^-?(0|[1-9]\\d*)(\\.\\d+)?$",
"description": "Decimal monetary amount at ISO 4217 minor-unit precision (string).",
"example": "100.00"
},
"currency": {
"type": "string",
"pattern": "^[A-Z]{3}$",
"description": "ISO 4217 currency code.",
"example": "USD"
}
}
}
]
},
"withinHours": {
"type": "integer",
"minimum": 1,
"maximum": 24,
"description": "Optional window in hours for the growth check (1–24).",
"example": 6
}
}
}
}
}
}
}
}
}
},
"example": {
"name": "Anomaly alerts",
"settings": {
"costAnomalies": {
"minSeverity": "warning",
"minExcessCost": {
"amount": "100.00",
"currency": "USD"
},
"allocations": {
"mode": "all",
"ids": []
}
}
}
}
}
Flags
| Flag | Type | Default | Example | Description |
|---|---|---|---|---|
--dry-run | boolean | false | If true, validates the request and returns the would-be result without applying it. The response then carries the X-Dry-Run header. |
Every command also accepts the CLI-wide flags for output shaping — see Output formats and Table output options.
Output
OK - Dry run only (dryRun=true). Every check of a real create passed and nothing was created;
id, createTime, updateTime, and etag are null.
By default dci renders the result as a table. Use --output json to get the full structure described below — see Output formats.
| Field | Type | Description |
|---|---|---|
id | string | Server-assigned notification id. null only on a dry-run create. |
name | string | Display name of the notification config. |
createTime | string (date-time) | When the config was created. null only on a dry-run create. |
updateTime | string (date-time) | When the config was last changed. null only on a dry-run create. |
etag | string | Current strong ETag. Send it unchanged in If-Match on update or delete. null only on a dry-run create. |
destinations | array of object | Server-managed destinations. Always one email entry with the target user's address. |
destinations[].type | string | Delivery channel. Personal delivery is email only. One of: "email". |
destinations[].address | string (email) | Target user's email, copied by the server. |
settings | object | Subscribed settings. |
settings.costAnomalies | object | Cost-anomaly notification settings: minimum severity, excess-cost threshold, allocation scope, and optional resend controls. |
settings.costAnomalies.minSeverity | string | warning — warning and above. critical — critical only. There is no information or all value; a write of all is rejected with 422. A read of a missing stored severity (console "All severity levels") and of warning both return warning. critical is stored and returned as critical. One of: "warning", "critical". |
settings.costAnomalies.minExcessCost | object | Minimum excess cost, as Money. Notify when excess cost is above this amount, not total spend. 100.00 USD means $100. currency must be USD; any other currency is rejected with 422. A negative amount is rejected with 422. A fractional amount is rejected with 422 (100.99 is not a whole number of dollars). 100 and 100.00 are both accepted. Amounts above 1,000,000,000 whole dollars, and forms such as Inf, exponents, a leading +, or underscores, are rejected with 422. Responses return the amount as "100.00". 0 is accepted. |
settings.costAnomalies.minExcessCost.amount | string | Decimal monetary amount at ISO 4217 minor-unit precision (string). |
settings.costAnomalies.minExcessCost.currency | string | ISO 4217 currency code. |
settings.costAnomalies.allocations | object | Allocation scope for cost-anomaly notifications. ids are allocation ids from GET /analytics/v1/allocations. An id outside the caller's tenant is rejected with 422 without saying whether it exists elsewhere. mode: all means every allocation with anomaly detection enabled, including anomalies that are not tied to an allocation, and it carries an empty ids. include and exclude require at least one id. ids are an ordered list: the server stores and returns them in the order sent, and it does not sort or drop entries. A duplicate id is rejected with 422. |
settings.costAnomalies.allocations.mode | string | all — every allocation with anomaly detection enabled (and unallocated anomalies). include — only the listed ids. exclude — all enabled allocations except the listed ids. One of: "all", "include", "exclude". |
settings.costAnomalies.allocations.ids | array of string | Allocation ids, in the order sent. A read returns that same order. A duplicate id is rejected with 422. Empty when mode is all. Non-empty when mode is include or exclude. |
settings.costAnomalies.repeat | object | Resend controls for an active cost anomaly. Omit the whole repeat object when neither control is set. An empty object is rejected with 422. |
settings.costAnomalies.repeat.everyHours | integer | Resend until the anomaly is inactive, every N hours (1–24). |
settings.costAnomalies.repeat.whenCostGrows | object | Resend when the anomaly's cost grows: - both percentIncrease and minIncrease present → match is required (or or and) - exactly one of percentIncrease or minIncrease present → omit match (percentage-only or value-only) - neither amount present → rejected with 422 percentIncrease is an integer percent (10 means 10 percent). Omit withinHours when the time window is off. Nested fields omitted — see the raw JSON schema. |
Example response (--output json)
{
"id": null,
"name": "Anomaly alerts",
"createTime": null,
"updateTime": null,
"etag": null,
"destinations": [
{
"type": "email",
}
],
"settings": {
"costAnomalies": {
"minSeverity": "warning",
"minExcessCost": {
"amount": "100.00",
"currency": "USD"
},
"allocations": {
"mode": "include",
"ids": [
"alloc_abc",
"alloc_def"
]
},
"repeat": {
"everyHours": 24,
"whenCostGrows": {
"match": "or",
"percentIncrease": 10,
"minIncrease": {
"amount": "150.00",
"currency": "USD"
},
"withinHours": 6
}
}
}
}
}
Raw JSON schema
{
"type": "object",
"additionalProperties": false,
"description": "Personal notification config for one user. Create and read return the full resource with the same\nfield names. Update and delete require `If-Match` against the strong `etag`.\n",
"required": [
"id",
"name",
"createTime",
"updateTime",
"etag",
"destinations",
"settings"
],
"properties": {
"id": {
"type": "string",
"readOnly": true,
"x-terraform-computed": true,
"description": "Server-assigned notification id. `null` only on a dry-run create.",
"nullable": true,
"example": "notif_01HX9P2KQVJ8Z3M4T5N6V7W8Y9"
},
"name": {
"type": "string",
"minLength": 1,
"maxLength": 200,
"description": "Display name of the notification config.",
"example": "Anomaly alerts"
},
"createTime": {
"type": "string",
"format": "date-time",
"readOnly": true,
"x-terraform-computed": true,
"description": "When the config was created. `null` only on a dry-run create.",
"nullable": true,
"example": "2026-10-01T08:00:00Z"
},
"updateTime": {
"type": "string",
"format": "date-time",
"readOnly": true,
"x-terraform-computed": true,
"description": "When the config was last changed. `null` only on a dry-run create.",
"nullable": true,
"example": "2026-10-01T08:00:00Z"
},
"etag": {
"type": "string",
"readOnly": true,
"x-terraform-computed": true,
"description": "Current strong ETag. Send it unchanged in `If-Match` on update or delete. `null` only on a dry-run\ncreate.\n",
"nullable": true,
"example": "3f7a1c9e2b4d6f8a0c1e3a5b7d9f1b3d5f7a9c1e3b5d7f9a1c3e5b7d9f1a3c5e"
},
"destinations": {
"type": "array",
"readOnly": true,
"x-terraform-computed": true,
"minItems": 1,
"maxItems": 1,
"description": "Server-managed destinations. Always one email entry with the target user's address.\n",
"items": {
"type": "object",
"additionalProperties": false,
"description": "Server-copied email destination. The create/update body does not accept an email address; the server\ncopies the target user's email.\n",
"required": [
"type",
"address"
],
"properties": {
"type": {
"type": "string",
"description": "Delivery channel. Personal delivery is email only.",
"enum": [
"email"
]
},
"address": {
"type": "string",
"format": "email",
"readOnly": true,
"x-doit-pii": true,
"x-terraform-computed": true,
"description": "Target user's email, copied by the server.",
}
}
},
"example": [
{
"type": "email",
}
]
},
"settings": {
"description": "Subscribed settings.",
"allOf": [
{
"type": "object",
"additionalProperties": false,
"description": "Subscribed notification settings. Only `costAnomalies` is accepted. Settings for any other type are\nrejected; omitting `costAnomalies` is rejected.\n",
"required": [
"costAnomalies"
],
"properties": {
"costAnomalies": {
"type": "object",
"additionalProperties": false,
"description": "Cost-anomaly notification settings: minimum severity, excess-cost threshold, allocation scope, and\noptional resend controls.\n",
"required": [
"minSeverity",
"minExcessCost",
"allocations"
],
"properties": {
"minSeverity": {
"type": "string",
"enum": [
"warning",
"critical"
],
"description": "`warning` — warning and above. `critical` — critical only. There is no `information` or `all`\nvalue; a write of `all` is rejected with `422`. A read of a missing stored severity (console \"All\nseverity levels\") and of warning both return `warning`. `critical` is stored and returned as\n`critical`.\n",
"example": "warning"
},
"minExcessCost": {
"description": "Minimum excess cost, as Money. Notify when excess cost is above this amount, not total spend.\n`100.00` USD means $100. `currency` must be `USD`; any other currency is rejected with `422`. A\nnegative amount is rejected with `422`. A fractional amount is rejected with `422` (`100.99` is not a\nwhole number of dollars). `100` and `100.00` are both accepted. Amounts above 1,000,000,000 whole\ndollars, and forms such as `Inf`, exponents, a leading `+`, or underscores, are rejected with `422`.\nResponses return the amount as `\"100.00\"`. `0` is accepted.\n",
"allOf": [
{
"type": "object",
"required": [
"amount",
"currency"
],
"properties": {
"amount": {
"type": "string",
"pattern": "^-?(0|[1-9]\\d*)(\\.\\d+)?$",
"description": "Decimal monetary amount at ISO 4217 minor-unit precision (string).",
"example": "100.00"
},
"currency": {
"type": "string",
"pattern": "^[A-Z]{3}$",
"description": "ISO 4217 currency code.",
"example": "USD"
}
}
}
]
},
"allocations": {
"type": "object",
"additionalProperties": false,
"description": "Allocation scope for cost-anomaly notifications. `ids` are allocation ids from `GET\n/analytics/v1/allocations`. An id outside the caller's tenant is rejected with `422` without saying\nwhether it exists elsewhere.\n\n`mode: all` means every allocation with anomaly detection enabled, including anomalies that are not\ntied to an allocation, and it carries an empty `ids`. `include` and `exclude` require at least one\nid. `ids` are an ordered list: the server stores and returns them in the order sent, and it does not\nsort or drop entries. A duplicate id is rejected with `422`.\n",
"required": [
"mode",
"ids"
],
"properties": {
"mode": {
"type": "string",
"enum": [
"all",
"include",
"exclude"
],
"description": "`all` — every allocation with anomaly detection enabled (and unallocated anomalies). `include` —\nonly the listed ids. `exclude` — all enabled allocations except the listed ids.\n",
"example": "include"
},
"ids": {
"type": "array",
"description": "Allocation ids, in the order sent. A read returns that same order. A duplicate id is rejected\nwith `422`. Empty when `mode` is `all`. Non-empty when `mode` is `include` or `exclude`.\n",
"items": {
"type": "string",
"minLength": 1
},
"example": [
"alloc_abc",
"alloc_def"
]
}
}
},
"repeat": {
"type": "object",
"additionalProperties": false,
"minProperties": 1,
"description": "Resend controls for an active cost anomaly. Omit the whole `repeat` object when neither control is\nset. An empty object is rejected with `422`.\n",
"properties": {
"everyHours": {
"type": "integer",
"minimum": 1,
"maximum": 24,
"description": "Resend until the anomaly is inactive, every N hours (1–24).\n",
"example": 24
},
"whenCostGrows": {
"type": "object",
"additionalProperties": false,
"minProperties": 1,
"description": "Resend when the anomaly's cost grows:\n\n- both `percentIncrease` and `minIncrease` present → `match` is\n required (`or` or `and`)\n- exactly one of `percentIncrease` or `minIncrease` present → omit\n `match` (percentage-only or value-only)\n- neither amount present → rejected with `422`\n\n`percentIncrease` is an integer percent (`10` means 10 percent). Omit `withinHours` when the time\nwindow is off.\n",
"properties": {
"match": {
"type": "string",
"enum": [
"or",
"and"
],
"description": "How to combine percent and dollar growth when both are set. Required when both amounts are present;\nomit when only one amount is present.\n",
"example": "or"
},
"percentIncrease": {
"type": "integer",
"minimum": 1,
"description": "Minimum percent cost increase that triggers a resend. `10` means 10 percent. Must be greater than 0.\n",
"example": 10
},
"minIncrease": {
"description": "Minimum cost growth that triggers a resend, as Money. Must be a positive whole number of dollars.\n`150.00` USD means $150. `currency` must be `USD`; any other currency is rejected with `422`. A\nnegative amount is rejected with `422`. A fractional amount is rejected with `422` (`150.99` is not a\nwhole number of dollars). `150` and `150.00` are both accepted. Amounts above 1,000,000,000 whole\ndollars, and forms such as `Inf`, exponents, a leading `+`, or underscores, are rejected with `422`.\nResponses return the amount as `\"150.00\"`.\n",
"allOf": [
{
"type": "object",
"required": [
"amount",
"currency"
],
"properties": {
"amount": {
"type": "string",
"pattern": "^-?(0|[1-9]\\d*)(\\.\\d+)?$",
"description": "Decimal monetary amount at ISO 4217 minor-unit precision (string).",
"example": "100.00"
},
"currency": {
"type": "string",
"pattern": "^[A-Z]{3}$",
"description": "ISO 4217 currency code.",
"example": "USD"
}
}
}
]
},
"withinHours": {
"type": "integer",
"minimum": 1,
"maximum": 24,
"description": "Optional window in hours for the growth check (1–24).",
"example": 6
}
}
}
}
}
}
}
}
}
]
}
}
}
Errors
On failure, dci prints a single error message — with a hint when one is available — and exits with a typed code your scripts can branch on. See Errors and exit codes for the full contract.
HTTP status to exit code mapping
| HTTP status | Exit code | Error code | Meaning |
|---|---|---|---|
| 400, 422 | 30 | VALIDATION_ERROR | The arguments or request body were rejected. Review the command's flags and payload. |
| 401 | 10 | AUTHENTICATION_FAILED | Not signed in, or the API token is invalid. Run dci login or check DCI_API_KEY. |
| 403 | 11 | PERMISSION_DENIED | The DoiT user or the active customer context does not have access. |
| 404 | 20 | RESOURCE_NOT_FOUND | The requested resource does not exist. Check the identifier argument. |
| 409 | 21 | RESOURCE_CONFLICT | The operation conflicts with the resource's current state. |
| 500 | 40 | API_SERVER_ERROR | The API failed to process the request. Retryable; contact DoiT support if it persists. |
Related
- delete-user-notification — Delete a personal notification
- get-user-notification — Get a personal notification
- list-user-notifications — List personal notifications for a user
- update-user-notification — Update a personal notification
- API reference: POST /iam/v1/users/{userId}/notifications
Aliases: createusernotification