curl --request POST \
--url https://api.indm.social/v1/automations/validate \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data @- <<EOF
{
"keyword": "GUIDE",
"dm_message": "Here's the spring planting guide. Enjoy it."
}
EOFconst options = {
method: 'POST',
headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'},
body: JSON.stringify({keyword: 'GUIDE', dm_message: 'Here\'s the spring planting guide. Enjoy it.'})
};
fetch('https://api.indm.social/v1/automations/validate', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));import requests
url = "https://api.indm.social/v1/automations/validate"
payload = {
"keyword": "GUIDE",
"dm_message": "Here's the spring planting guide. Enjoy it."
}
headers = {
"Authorization": "Bearer <token>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text){
"specification": {
"trigger": "comment",
"keyword": "GUIDE",
"public_reply": "<string>",
"button_label": "Send me the guide",
"dm_message": "Here's the spring planting guide. Enjoy it.",
"link_url": "https://example.com/spring-guide",
"language": "en",
"status_when_created": "draft"
},
"ready": true,
"problems": [],
"notes": [
{
"code": "keywords_case_insensitive",
"message": "Keywords are case-insensitive, so GUIDE also matches guide and Guide."
},
{
"code": "created_as_draft",
"message": "Created as a draft. Nothing sends until it is set live."
}
]
}{
"error": {
"type": "invalid_request",
"code": "button_label_too_long",
"message": "The button label is 26 characters. Instagram shows up to 20, so it would be cut off.",
"param": "button_label",
"doc_url": "https://docs.indm.social/errors#button_label_too_long"
}
}{
"error": {
"type": "authentication_error",
"code": "api_key_expired",
"message": "This API key expired on 3 Feb 2027. Create a new one under Settings → API keys.",
"doc_url": "https://docs.indm.social/errors#api_key_expired"
}
}{
"error": {
"type": "rate_limited",
"code": "rate_limited",
"message": "That's more than 120 requests a minute on one key. Wait 18s and try again.",
"doc_url": "https://docs.indm.social/errors#rate_limited"
}
}{
"error": {
"type": "api_error",
"code": "internal_error",
"message": "Something went wrong on our end. Try again; if it keeps happening, quote request req_8Qm2ZcY4vN7tLp1K to support@indm.social.",
"doc_url": "https://docs.indm.social/errors#internal_error"
}
}Validate an automation
A dry run. Checks a proposed comment-to-DM automation against the rules the engine enforces and returns the normalised version together with anything that would make it misfire: a keyword with a space in it, a button label Instagram would cut off, a DM that promises a link it was never given.
It writes nothing, needs no post, and works with a read key, so you can check copy
before you have anywhere to put it.
Needs a read or full key.
curl --request POST \
--url https://api.indm.social/v1/automations/validate \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data @- <<EOF
{
"keyword": "GUIDE",
"dm_message": "Here's the spring planting guide. Enjoy it."
}
EOFconst options = {
method: 'POST',
headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'},
body: JSON.stringify({keyword: 'GUIDE', dm_message: 'Here\'s the spring planting guide. Enjoy it.'})
};
fetch('https://api.indm.social/v1/automations/validate', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));import requests
url = "https://api.indm.social/v1/automations/validate"
payload = {
"keyword": "GUIDE",
"dm_message": "Here's the spring planting guide. Enjoy it."
}
headers = {
"Authorization": "Bearer <token>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text){
"specification": {
"trigger": "comment",
"keyword": "GUIDE",
"public_reply": "<string>",
"button_label": "Send me the guide",
"dm_message": "Here's the spring planting guide. Enjoy it.",
"link_url": "https://example.com/spring-guide",
"language": "en",
"status_when_created": "draft"
},
"ready": true,
"problems": [],
"notes": [
{
"code": "keywords_case_insensitive",
"message": "Keywords are case-insensitive, so GUIDE also matches guide and Guide."
},
{
"code": "created_as_draft",
"message": "Created as a draft. Nothing sends until it is set live."
}
]
}{
"error": {
"type": "invalid_request",
"code": "button_label_too_long",
"message": "The button label is 26 characters. Instagram shows up to 20, so it would be cut off.",
"param": "button_label",
"doc_url": "https://docs.indm.social/errors#button_label_too_long"
}
}{
"error": {
"type": "authentication_error",
"code": "api_key_expired",
"message": "This API key expired on 3 Feb 2027. Create a new one under Settings → API keys.",
"doc_url": "https://docs.indm.social/errors#api_key_expired"
}
}{
"error": {
"type": "rate_limited",
"code": "rate_limited",
"message": "That's more than 120 requests a minute on one key. Wait 18s and try again.",
"doc_url": "https://docs.indm.social/errors#rate_limited"
}
}{
"error": {
"type": "api_error",
"code": "internal_error",
"message": "Something went wrong on our end. Try again; if it keeps happening, quote request req_8Qm2ZcY4vN7tLp1K to support@indm.social.",
"doc_url": "https://docs.indm.social/errors#internal_error"
}
}Authorizations
An API key from Settings → API keys in the inDM app, sent as
Authorization: Bearer indm_.... Keys belong to one workspace and carry a scope:
read for GET routes and the validate dry run, full for everything. Keep keys on
your server; never ship one to a browser or a mobile app.
Body
A proposed automation to check. Nothing is saved.
The one word a fan comments to trigger it. Case-insensitive.
"GUIDE"
The direct message the fan receives.
"Here's the spring planting guide. Enjoy it."
What you want to happen, in your own words.
"Send the spring guide to anyone who comments GUIDE"
The opt-in button's label. Up to 20 characters.
"Send me the guide"
A public reply posted under the fan's comment.
The link the DM delivers.
"https://example.com/spring-guide"
The two-letter code of the language the copy is written in, such as en, es or ko.
"en"
Response
The check. ready is true when nothing would stop it working.
The normalised automation, as inDM would build it.
Show child attributes
Show child attributes
True when problems is empty, so nothing would stop it working.
true
What would make it misfire, in the order found. Empty when ready is true.
Show child attributes
Show child attributes
[]
Facts about how it will behave once live.
Show child attributes
Show child attributes
[
{
"code": "keywords_case_insensitive",
"message": "Keywords are case-insensitive, so GUIDE also matches guide and Guide."
},
{
"code": "created_as_draft",
"message": "Created as a draft. Nothing sends until it is set live."
}
]