{"id":3767,"date":"2025-05-13T07:02:30","date_gmt":"2025-05-13T07:02:30","guid":{"rendered":"https:\/\/mailitics.com\/index.php\/2025\/05\/13\/pause-your-ml-pipelines-for-human-review-using-aws-step-functions-slack\/"},"modified":"2025-05-13T07:02:30","modified_gmt":"2025-05-13T07:02:30","slug":"pause-your-ml-pipelines-for-human-review-using-aws-step-functions-slack","status":"publish","type":"post","link":"https:\/\/mailitics.com\/index.php\/2025\/05\/13\/pause-your-ml-pipelines-for-human-review-using-aws-step-functions-slack\/","title":{"rendered":"Pause Your ML Pipelines for Human Review Using AWS Step Functions + Slack"},"content":{"rendered":"<p>    Pause Your ML Pipelines for Human Review Using AWS Step Functions + Slack<br \/>\n \t<BR><br \/>\n<BR><\/BR><br \/>\n    <!-- no image --><br \/>\n \t<BR><br \/>\n<BR><\/BR><\/p>\n<div>\n<p class=\"wp-block-paragraph\"><mdspan datatext=\"el1746666014364\" class=\"mdspan-comment\">Have you ever<\/mdspan> wanted to pause an automated workflow to wait for a human decision?<\/p>\n<p class=\"wp-block-paragraph\">Maybe you need approval before provisioning cloud resources, promoting a machine learning model to production, or charging a customer\u2019s credit card.<\/p>\n<p class=\"wp-block-paragraph\">In many\u00a0<strong>data science and machine learning workflows<\/strong>, automation gets you 90% of the way \u2014 but that critical last step often needs human judgment.<\/p>\n<p class=\"wp-block-paragraph\">Especially in production environments,\u00a0<strong>model retraining<\/strong>,\u00a0<strong>anomaly overrides<\/strong>, or\u00a0<strong>large data movements<\/strong>\u00a0require careful human review to avoid expensive mistakes.<\/p>\n<p class=\"wp-block-paragraph\">In my case, I needed to manually review situations where my system flagged\u00a0<strong>more than 6% of customer data for anomalies<\/strong>\u00a0\u2014 often due to accidental pushes by customers.<\/p>\n<p class=\"wp-block-paragraph\">Before I implemented a proper workflow, this was handled informally: developers would directly update production databases (!) \u2014 risky, error-prone, and unscalable.<\/p>\n<p class=\"wp-block-paragraph\">To solve this, I built a\u00a0<strong>scalable manual approval system<\/strong>\u00a0using\u00a0<strong><a href=\"https:\/\/towardsdatascience.com\/tag\/aws\/\" title=\"AWS\">AWS<\/a> Step Functions<\/strong>,\u00a0<strong>Slack<\/strong>,\u00a0<strong>Lambda<\/strong>, and\u00a0<strong>SNS<\/strong>\u00a0\u2014 a cloud-native, low-cost architecture that cleanly paused workflows for human approvals without spinning up idle compute.<\/p>\n<p class=\"wp-block-paragraph\">In this post, I\u2019ll walk you through the full design, the AWS resources involved, and how you can apply it to your own critical workflows. <\/p>\n<p class=\"wp-block-paragraph\">Let\u2019s get into it <img data-recalc-dims=\"1\" decoding=\"async\" src=\"https:\/\/i0.wp.com\/s.w.org\/images\/core\/emoji\/15.0.3\/72x72\/1f447.png?ssl=1\" alt=\"\ud83d\udc47\" class=\"wp-smiley\" style=\"height: 1em; max-height: 1em;\"><\/p>\n<h2 class=\"wp-block-heading\">The Solution<\/h2>\n<p class=\"wp-block-paragraph\">My application is deployed in the AWS ecosystem, so we\u2019ll use <a href=\"https:\/\/towardsdatascience.com\/tag\/aws-step-functions\/\" title=\"Aws Step Functions\">Aws Step Functions<\/a> to build a state machine that:<\/p>\n<ol class=\"wp-block-list\">\n<li class=\"wp-block-list-item\">Executes business logic<\/li>\n<li class=\"wp-block-list-item\">Lambda with\u00a0<code>WaitForTaskToken<\/code>\u00a0to pause until approval<\/li>\n<li class=\"wp-block-list-item\">Sends a Slack message requesting approval (can be an email\/)<\/li>\n<li class=\"wp-block-list-item\">Waits for a human to click \u201cApprove\u201d or \u201cReject\u201d<\/li>\n<li class=\"wp-block-list-item\">Resumes automatically from the same point<\/li>\n<\/ol>\n<figure class=\"wp-block-image size-full\"><img data-recalc-dims=\"1\" decoding=\"async\" src=\"https:\/\/i0.wp.com\/contributor.insightmediagroup.io\/wp-content\/uploads\/2025\/05\/1_y460FPYizzRLYDyIWsZIYA-1.png?ssl=1\" alt=\"\" class=\"wp-image-603826\"><figcaption class=\"wp-element-caption\">The Step function flow<\/figcaption><\/figure>\n<p class=\"wp-block-paragraph\">Here is a youtube video showing the demo and actual application in action:<\/p>\n<figure class=\"wp-block-embed is-type-video is-provider-youtube wp-block-embed-youtube wp-embed-aspect-4-3 wp-has-aspect-ratio\">\n<div class=\"wp-block-embed__wrapper\">\n<iframe loading=\"lazy\" title=\"Manual Approval Workflow in AWS Step Functions with Slack, SNS, and\u00a0Lambda\" width=\"500\" height=\"375\" src=\"https:\/\/www.youtube.com\/embed\/bor4HFLvE4Q?feature=oembed\" frameborder=\"0\" allow=\"accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share\" referrerpolicy=\"strict-origin-when-cross-origin\" allowfullscreen><\/iframe>\n<\/div>\n<\/figure>\n<p class=\"wp-block-paragraph\">I have also hosted the live <strong>demo app here<\/strong>\u00a0\u2192<br \/><img data-recalc-dims=\"1\" decoding=\"async\" src=\"https:\/\/i0.wp.com\/s.w.org\/images\/core\/emoji\/15.0.3\/72x72\/1f449.png?ssl=1\" alt=\"\ud83d\udc49\" class=\"wp-smiley\" style=\"height: 1em; max-height: 1em;\">\u00a0<a href=\"https:\/\/kzmnoafk9udhsli0sqwj.lite.vusercontent.net\/\" target=\"_blank\" rel=\"noreferrer noopener\">https:\/\/v0-manual-review-app-fwtjca.vercel.app<\/a><br \/>All code is hosted\u00a0<a href=\"https:\/\/gist.github.com\/akritaag\/1bf927c727c9cdfc769e63c604d9a9eb\" target=\"_blank\" rel=\"noreferrer noopener\">here<\/a>\u00a0with the right set of IAM permissions.<\/p>\n<hr class=\"wp-block-separator has-alpha-channel-opacity is-style-dotted\">\n<h3 class=\"wp-block-heading\">Step-by-Step Implementation<\/h3>\n<ol class=\"wp-block-list\">\n<li class=\"wp-block-list-item\">Now we will create the Step Function with a manual review flow step. Here is the step function definition:<\/li>\n<\/ol>\n<figure class=\"wp-block-image size-large\"><img data-recalc-dims=\"1\" height=\"491\" width=\"1024\" decoding=\"async\" src=\"https:\/\/i0.wp.com\/contributor.insightmediagroup.io\/wp-content\/uploads\/2025\/05\/1_2y4alluwPtct_18haN9Lyw-1024x491.png?resize=1024%2C491&#038;ssl=1\" alt=\"\" class=\"wp-image-603827\"><figcaption class=\"wp-element-caption\">Step function flow with definition<\/figcaption><\/figure>\n<p class=\"wp-block-paragraph\">The flow above generates a dataset, uploads it to AWS S3 and if a review is required, then invokes the Manual Review lambda. On the manual review step, we\u2019ll use a Task lambda with an invoke on\u00a0<code>WaitForTaskToken<\/code>, which pauses execution until resumed. The lambda reads the token this way:<\/p>\n<pre class=\"wp-block-prismatic-blocks\"><code class=\"language-&lt;a href=\" https: title=\"Python\">Python\"&gt;def lambda_handler(event, context):\n\n  config = event[\"Payload\"][\"config\"]\n  task_token = event[\"Payload\"][\"taskToken\"] # Step Functions auto-generates this\n\n  reviewer = ManualReview(config, task_token)\n  reviewer.send_notification()\n\n  return config<\/code><\/pre>\n<p class=\"wp-block-paragraph\">This Lambda sends a Slack message that includes the task token so the function knows what execution to resume.<\/p>\n<p class=\"wp-block-paragraph\">2. Before the we send out the slack notification, we need to <\/p>\n<ol class=\"wp-block-list\">\n<li class=\"wp-block-list-item\">setup an SNS Topic that receives review messages from the lambda<\/li>\n<li class=\"wp-block-list-item\">a slack workflow with a web-hook subscribed to the SNS topic, and a confirmed subscription<\/li>\n<li class=\"wp-block-list-item\">an https API Gateway with\u00a0<code>approval<\/code>\u00a0and\u00a0<code>rejection<\/code>\u00a0endpoints.<\/li>\n<li class=\"wp-block-list-item\">a lambda function that processes the API Gateway requests:\u00a0<a href=\"https:\/\/gist.github.com\/akritaag\/1bf927c727c9cdfc769e63c604d9a9eb#file-api_review_processor-py\" target=\"_blank\" rel=\"noreferrer noopener\">code<\/a>\n<\/li>\n<\/ol>\n<p class=\"wp-block-paragraph\">I followed the youtube video here for my setup.<\/p>\n<figure class=\"wp-block-embed is-type-video is-provider-youtube wp-block-embed-youtube wp-embed-aspect-16-9 wp-has-aspect-ratio\">\n<div class=\"wp-block-embed__wrapper\">\n<iframe loading=\"lazy\" title=\"AWS SNS with Slack WITHOUT AWS Lambda\" width=\"500\" height=\"281\" src=\"https:\/\/www.youtube.com\/embed\/CszzQcPAqNM?feature=oembed\" frameborder=\"0\" allow=\"accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share\" referrerpolicy=\"strict-origin-when-cross-origin\" allowfullscreen><\/iframe>\n<\/div>\n<\/figure>\n<p class=\"wp-block-paragraph\">3. Once the above is setup, setup the variables into the web-hook step of the slack workflow:<\/p>\n<figure class=\"wp-block-image size-large\"><img data-recalc-dims=\"1\" height=\"688\" width=\"1024\" decoding=\"async\" src=\"https:\/\/i0.wp.com\/contributor.insightmediagroup.io\/wp-content\/uploads\/2025\/05\/1_4wjoKuSg4x3wtVsTUNDZUQ-1024x688.png?resize=1024%2C688&#038;ssl=1\" alt=\"\" class=\"wp-image-603829\"><\/figure>\n<p class=\"wp-block-paragraph\"><span style=\"background-color: var(--wp--preset--color--surface-primary); color: var(--wp--custom--color--text--primary); font-size: revert;\">And use the variables with a helpful note in the following step:<\/span><\/p>\n<p class=\"wp-block-paragraph\"><img data-recalc-dims=\"1\" fetchpriority=\"high\" decoding=\"async\" width=\"700\" height=\"462\" src=\"https:\/\/i0.wp.com\/miro.medium.com\/v2\/resize%3Afit%3A1400\/1%2AnKYHQelmXIKbNbOUwMRdYw.png?resize=700%2C462&#038;ssl=1\" alt=\"\"><\/p>\n<p class=\"wp-block-paragraph\">The final workflow will look like this:<\/p>\n<p class=\"wp-block-paragraph\"><img data-recalc-dims=\"1\" loading=\"lazy\" decoding=\"async\" width=\"700\" height=\"439\" src=\"https:\/\/i0.wp.com\/miro.medium.com\/v2\/resize%3Afit%3A1400\/1%2AVZarrTuYYnANWov-bQNVXQ.png?resize=700%2C439&#038;ssl=1\" alt=\"\"><\/p>\n<p class=\"wp-block-paragraph\">4. Send a Slack Notification published to an SNS topic (you can alternately use slack-sdk as well) with job parameters. Here is what the message will look like:<\/p>\n<pre class=\"wp-block-prismatic-blocks\"><code class=\"language-python\">def publish_message(self, bucket_name: str, s3_file: str, subject: str = \"Manual Review\") -&gt; dict:\n\n    presigned_url = S3.generate_presigned_url(bucket_name, s3_file, expiration=86400)  # 1 day expiration\n\n    message = {\n        \"approval_link\": self.approve_link,\n        \"rejection_link\": self.reject_link,\n        \"s3_file\": presigned_url if presigned_url else s3_file\n    }\n\n    logging.info(f\"Publishing message to &lt;{self.topic_arn}&gt;, with subject: {subject}, message: {message}\")\n\n    response = self.client.publish(\n        TopicArn=self.topic_arn,\n        Message=json.dumps(message),\n        Subject=subject\n    )\n\n    logging.info(f\"Response: {response}\")\n    return response<\/code><\/pre>\n<p class=\"wp-block-paragraph\">This Lambda sends a Slack message that includes the task token so the function knows what execution to resume.<\/p>\n<pre class=\"wp-block-prismatic-blocks\"><code class=\"language-python\">def send_notification(self):\n\n    # As soon as this message is sent out, this callback lambda will go into a wait state,\n    # until an explicit call to this Lambda function execution is triggered.\n\n    # If you don't want this function to wait forever (or the default Steps timeout), ensure you setup\n    # an explicit timeout on this\n    self.sns.publish_message(self.s3_bucket_name, self.s3_key)\n\ndef lambda_handler(event, context):\n\n    config = event[\"Payload\"][\"config\"]\n    task_token = event[\"Payload\"][\"taskToken\"]  # Step Functions auto-generates this\n\n    reviewer = ManualReview(config, task_token)\n    reviewer.send_notification()<\/code><\/pre>\n<p class=\"wp-block-paragraph\">5. Once a review notification is received in slack, the user can approve or reject it. The step function goes into a wait state until it receives a user response; however the task token is set to expire in 24 hours, so inactivity will timeout the step function.<\/p>\n<p class=\"wp-block-paragraph\">Based on whether the user approves or rejects the review request, the rawPath gets set and can be parsed here:\u00a0<a href=\"https:\/\/gist.github.com\/akritaag\/1bf927c727c9cdfc769e63c604d9a9eb#file-api_review_processor-py\" rel=\"noreferrer noopener\" target=\"_blank\">code<\/a><\/p>\n<pre class=\"wp-block-prismatic-blocks\"><code class=\"language-python\">action = event.get(\"rawPath\", \"\").strip(\"\/\").lower()  \n# Extracts 'approve' or 'reject'<\/code><\/pre>\n<p class=\"wp-block-paragraph\" id=\"7d1a\">The receiving API Gateway + Lambda combo:<\/p>\n<ul class=\"wp-block-list\">\n<li class=\"wp-block-list-item\">Parses the Slack payload<\/li>\n<li class=\"wp-block-list-item\">Extracts\u00a0<code>taskToken<\/code>\u00a0+ decision<\/li>\n<li class=\"wp-block-list-item\">Uses\u00a0<code>StepFunctions.send_task_success()<\/code>\u00a0or\u00a0<code>send_task_failure()<\/code>\n<\/li>\n<\/ul>\n<p class=\"wp-block-paragraph\">Example:<\/p>\n<pre class=\"wp-block-prismatic-blocks\"><code class=\"language-python\">match action:\n    case \"approve\":\n        output_dict[\"is_manually_approved\"] = True\n        response_message = \"Approval processed successfully.\"\n    case \"reject\":\n        output_dict[\"is_manually_rejected\"] = True\n        response_message = \"Rejection processed successfully.\"\n    case _:\n        return {\n            \"statusCode\": 400,\n            \"body\": json.dumps({\"error\": \"Invalid action. Use '\/approve' or '\/reject' in URL.\"})\n        }\n\n...\n\nsfn_client.send_task_success(\n    taskToken=task_token,\n    output=output\n)<\/code><\/pre>\n<p class=\"wp-block-paragraph\" id=\"c828\"><strong>Note<\/strong>: Lambda configured with\u00a0<code>WaitForTaskToken<\/code>\u00a0<strong>must<\/strong>\u00a0wait. If you don\u2019t send the token, your workflow just stalls.<\/p>\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\">\n<p class=\"wp-block-paragraph\" id=\"9345\">Bonus: If you need email or SMS alerts, use\u00a0<strong>SNS<\/strong>\u00a0to notify a broader group.<br \/>Just\u00a0<code>sns.publish()<\/code>\u00a0from within your Lambda or Step Function.<\/p>\n<\/blockquote>\n<h3 class=\"wp-block-heading\">Testing<\/h3>\n<p class=\"wp-block-paragraph\">Once the manual approval system was wired up, it was time to kick the tires. Here\u2019s how I tested it:<\/p>\n<ul class=\"wp-block-list\">\n<li class=\"wp-block-list-item\">Right after publishing the slack workflow, I confirmed the SNS subscription before messages get forwarded. Don\u2019t skip this step.<\/li>\n<li class=\"wp-block-list-item\">Then, I triggered the Step Function manually with a fake payload simulating a data flagging event.<\/li>\n<li class=\"wp-block-list-item\">When the workflow hit the manual approval step, it sent a Slack message with Approve\/Reject buttons.<\/li>\n<\/ul>\n<p class=\"wp-block-paragraph\">I tested\u00a0<strong>all major paths<\/strong>:<\/p>\n<ul class=\"wp-block-list\">\n<li class=\"wp-block-list-item\">\n<strong>Approve<\/strong>: Clicked Approve \u2014 saw the Step Function resume and complete successfully.<\/li>\n<li class=\"wp-block-list-item\">\n<strong>Reject<\/strong>: Clicked Reject \u2014 Step Function moved cleanly into a failure state.<\/li>\n<li class=\"wp-block-list-item\">\n<strong>Timeout<\/strong>: Ignored the Slack message \u2014 Step Function waited for the configured timeout and then gracefully timed out without hanging.<\/li>\n<\/ul>\n<p class=\"wp-block-paragraph\" id=\"9faa\">Behind the scenes, I also verified that:<\/p>\n<ul class=\"wp-block-list\">\n<li class=\"wp-block-list-item\">The Lambda receiving Slack responses was correctly parsing action payloads.<\/li>\n<li class=\"wp-block-list-item\">No rogue task tokens were left hanging.<\/li>\n<li class=\"wp-block-list-item\">Step Functions metrics and Slack error logs were clean.<\/li>\n<\/ul>\n<p class=\"wp-block-paragraph\">I highly recommend testing not just happy paths, but also \u201cwhat if nobody clicks?\u201d and \u201cwhat if Slack glitches?\u201d \u2014 catching these edge cases early saved me headaches later.<\/p>\n<hr class=\"wp-block-separator has-alpha-channel-opacity is-style-dotted\">\n<h3 class=\"wp-block-heading\" id=\"317f\">Lessons Learned<\/h3>\n<ul class=\"wp-block-list\">\n<li class=\"wp-block-list-item\">\n<strong>Always use timeouts<\/strong>: Set a timeout both on the\u00a0<code>WaitForTaskToken<\/code>\u00a0step\u00a0<strong>and<\/strong>\u00a0on the entire Step Function. Without it, workflows can get stuck indefinitely if no one responds.<\/li>\n<li class=\"wp-block-list-item\">\n<strong>Pass necessary context<\/strong>: If your Step Function needs certain files, paths, or config settings after resuming, make sure you\u00a0<strong>encode and send them along<\/strong>\u00a0in the SNS notification.<br \/>Step Functions\u00a0<em>do not<\/em>\u00a0automatically retain previous in-memory context when resuming from a Task Token.<\/li>\n<li class=\"wp-block-list-item\">\n<strong>Manage Slack noise<\/strong>: Be careful about spamming a Slack channel with too many review requests. I recommend creating\u00a0<strong>separate channels<\/strong>\u00a0for development, UAT, and production flows to keep things clean.<\/li>\n<li class=\"wp-block-list-item\">\n<strong>Lock down permissions early<\/strong>: Make sure all your AWS resources (Lambda functions, API Gateway, S3 buckets, SNS Topics) have\u00a0<strong>correct and minimal permissions\u00a0<\/strong>following the principle of least privilege. Where I needed to customize beyond AWS\u2019s defaults, I wrote and posted inline IAM policies as JSON. (You\u2019ll find examples in the\u00a0<a href=\"https:\/\/medium.com\/@akrita\/building-a-manual-approval-workflow-in-aws-step-functions-with-slack-sns-and-lambda-ef4189944f14#\">GitHub repo<\/a>).<\/li>\n<li class=\"wp-block-list-item\">\n<strong>Pre-sign and shorten URLs<\/strong>: If you\u2019re sending links (e.g., to S3 files) in Slack messages, pre-sign the URLs for secure access \u2014\u00a0<strong>and<\/strong>\u00a0shorten them for a cleaner Slack UI. Here\u2019s a quick example I used:<\/li>\n<\/ul>\n<pre class=\"wp-block-prismatic-blocks\"><code class=\"language-python\">shorten_url = requests.get(f\"http:\/\/tinyurl.com\/api-create.php?url={presigned_url}\").text\ndefault_links[key] = shorten_url if shorten_url else presigned_url<\/code><\/pre>\n<h3 class=\"wp-block-heading\" id=\"38d6\">Wrapping Up<\/h3>\n<p class=\"wp-block-paragraph\" id=\"cf36\">Adding human-in-the-loop logic doesn\u2019t have to mean duct tape and cron jobs. With\u00a0<strong>Step Functions + Slack<\/strong>, you can build reviewable, traceable, and\u00a0<strong>production-safe approval flows<\/strong>.<\/p>\n<p class=\"wp-block-paragraph\" id=\"b4bd\">If this helped, or you\u2019re trying something similar, drop a note in the comments! Let\u2019s build better workflows.\u00a0<\/p>\n<p class=\"wp-block-paragraph\"><strong>Note<\/strong>: <em>All images in this article were created by the author<\/em><\/p>\n<p>The post <a href=\"https:\/\/towardsdatascience.com\/pause-your-ml-pipelines-for-human-review-using-aws-step-functions-slack\/\">Pause Your ML Pipelines for Human Review Using AWS Step Functions + Slack<\/a> appeared first on <a href=\"https:\/\/towardsdatascience.com\/\">Towards Data Science<\/a>.<\/p>\n<\/div>\n<p> \t<BR><br \/>\n <BR><\/BR><br \/>\n    Akrita Agarwal<br \/>\n \t<BR><br \/>\n<BR><\/BR><br \/>\n<a href=\"https:\/\/towardsdatascience.com\/pause-your-ml-pipelines-for-human-review-using-aws-step-functions-slack\/\">Go to original source<\/a><br \/>\n \t<BR><br \/>\n <BR><\/BR><\/p>\n","protected":false},"excerpt":{"rendered":"<p>Pause Your ML Pipelines for Human Review Using AWS Step Functions + Slack Have you ever wanted to pause an automated workflow to wait for a human decision? Maybe you need approval before provisioning cloud resources, promoting a machine learning model to production, or charging a customer\u2019s credit card. In many\u00a0data science and machine learning [&hellip;]<\/p>\n","protected":false},"author":2,"featured_media":0,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[62,2418,2652,83,70,222,157],"tags":[535,2031,430],"class_list":["post-3767","post","type-post","status-publish","format-standard","hentry","category-aimldsaimlds","category-aws","category-aws-step-functions","category-data-science","category-machine-learning","category-mlops","category-python","tag-human","tag-review","tag-step"],"_links":{"self":[{"href":"https:\/\/mailitics.com\/index.php\/wp-json\/wp\/v2\/posts\/3767"}],"collection":[{"href":"https:\/\/mailitics.com\/index.php\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/mailitics.com\/index.php\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/mailitics.com\/index.php\/wp-json\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/mailitics.com\/index.php\/wp-json\/wp\/v2\/comments?post=3767"}],"version-history":[{"count":0,"href":"https:\/\/mailitics.com\/index.php\/wp-json\/wp\/v2\/posts\/3767\/revisions"}],"wp:attachment":[{"href":"https:\/\/mailitics.com\/index.php\/wp-json\/wp\/v2\/media?parent=3767"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/mailitics.com\/index.php\/wp-json\/wp\/v2\/categories?post=3767"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/mailitics.com\/index.php\/wp-json\/wp\/v2\/tags?post=3767"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}