{"slug": "your-api-returned-200-ok-your-ai-agent-still-failed", "title": "Your API Returned 200 OK. Your AI Agent Still Failed.", "summary": "A developer argues that HTTP 200 responses are an insufficient success metric for AI agents that execute real-world actions, since an agent can call an API correctly while choosing the wrong resource or duplicating side effects. The writeup proposes splitting agent success into three layers — technical request completion, backend operation execution, and semantic correctness of the action on the right resource for the right user — and warns that model-driven retries after timeouts can produce multiple real-world effects from a single user intent.", "body_md": "For most backend systems, `200 OK` is comforting.\n\nIt means the request reached the server, passed validation, and completed successfully.\n\nFor an AI agent, however, `200 OK` can hide one of the most dangerous failure modes in modern software:\n\n**The API did exactly what the agent asked — but the agent asked for the wrong thing.**\n\nImagine an AI-powered banking assistant.\n\nA customer says:\n\n“Refund the duplicate payment from yesterday.”\n\nThe agent retrieves several transactions, identifies what it believes is the duplicate, and calls:\n\n```\nPOST /refunds\n```\n\nThe request is authenticated.\n\nThe agent is authorised.\n\nThe transaction ID exists.\n\nThe refund API processes the request successfully.\n\n```\nHTTP/1.1 200 OK\n```\n\nEvery technical dashboard is green.\n\nBut the agent selected the wrong transaction.\n\nThe API succeeded.\n\nThe business outcome failed.\n\nAs AI systems evolve from chatbots that **recommend** actions into agents that **execute** them, backend engineers need to rethink what “success” actually means.\n\nTraditional systems usually measure success at several technical levels.\n\nAt the network layer:\n\n```\nDid the request reach the service?\n```\n\nAt the API layer:\n\n```\nDid the service return a successful response?\n```\n\nAt the database layer:\n\n```\nDid the transaction commit?\n```\n\nThat works reasonably well when deterministic application code has already decided what operation should happen.\n\nFor example:\n\n```\nrefundService.refund(transactionId);\n```\n\nThe developer has chosen:\n\nAgentic systems change this relationship.\n\nAn AI agent may be given tools such as:\n\n```\nfindCustomer()\nlookupTransaction()\nissueRefund()\ncancelOrder()\nsendEmail()\ndisableAccount()\nrestartService()\n```\n\nThe model then determines:\n\n```\nWhich tool should I call?\nWhich parameters should I use?\nShould I retry?\nWhat should I do next?\n```\n\nWe have introduced probabilistic reasoning **before deterministic side effects**.\n\nThat means we need another definition of success.\n\nI find it useful to separate agent success into three layers.\n\nDid the technical request complete?\n\n```\nHTTP/1.1 200 OK\n```\n\nDid the backend perform the requested operation?\n\n```\nRefund created successfully.\n```\n\nDid the system perform the **right action**, on the **right resource**, for the **right user**, under the **right conditions**, exactly as intended?\n\nFor a refund that might mean:\n\n```\nCorrect customer\nCorrect transaction\nCorrect amount\nCorrect reason\nCorrect approval\nExactly once\n```\n\nThe first two are familiar engineering problems.\n\nThe third becomes much more important once AI starts selecting and sequencing actions dynamically.\n\nConsider this user request:\n\n“Refund the most recent duplicate charge.”\n\nThe agent receives:\n\n```\n[\n  {\n    \"id\": \"TX-18419\",\n    \"amount\": 2500,\n    \"merchant\": \"ABC Store\"\n  },\n  {\n    \"id\": \"TX-18491\",\n    \"amount\": 2500,\n    \"merchant\": \"ABC Store\"\n  }\n]\n```\n\nThe agent incorrectly chooses:\n\n```\nTX-18491\n```\n\nand sends:\n\n```\n{\n  \"transactionId\": \"TX-18491\",\n  \"amount\": 2500\n}\n```\n\nThe backend validates the request.\n\nThe account has sufficient authority.\n\nThe transaction exists.\n\nThe refund executes.\n\nTechnically, there is no error.\n\nBut the customer wanted another transaction refunded.\n\nThis is an important distinction:\n\n**API correctness does not guarantee semantic correctness.**\n\nThe service knows how to refund a transaction.\n\nIt does not necessarily know whether the AI chose the correct transaction.\n\nNow imagine the refund really is correct.\n\nThe agent calls the service.\n\n```\nAgent → Refund API\n```\n\nThe refund succeeds.\n\nBut the response is lost.\n\nThe agent sees:\n\n```\nTimeout\n```\n\nIt reasons:\n\n“The refund probably failed. I should retry.”\n\nThe second call also succeeds.\n\nWithout protection, one user intent may produce multiple real-world side effects.\n\nThis problem is familiar to payment engineers and distributed-systems developers.\n\nThe difference is that with autonomous agents, retries may not come from a predefined retry library.\n\nThe model itself can decide:\n\n“Let me try that again.”\n\nThat makes idempotency even more important.\n\nWe already use identifiers such as:\n\n```\nrequest_id\ntrace_id\nspan_id\n```\n\nThose identify technical execution.\n\nAgents also need something representing the **business objective**.\n\n```\nintent_id = REFUND_DUPLICATE_CHARGE_8472\n```\n\nOne intent may generate many technical requests:\n\n```\nREFUND_DUPLICATE_CHARGE_8472\n        |\n        +-- lookup transaction\n        |\n        +-- validate eligibility\n        |\n        +-- create refund\n        |\n        +-- update CRM\n        |\n        +-- notify customer\n```\n\nSuppose the refund API times out.\n\nInstead of asking:\n\n```\nShould I POST /refunds again?\n```\n\nthe system can ask:\n\n```\nHas REFUND_DUPLICATE_CHARGE_8472\nalready produced a successful refund?\n```\n\nThat is a much safer abstraction.\n\nConceptually:\n\n```\npublic record AgentIntent(\n    UUID intentId,\n    String userId,\n    String action,\n    String resourceId,\n    IntentStatus status,\n    String resultId\n) {}\n```\n\nWith:\n\n```\npublic enum IntentStatus {\n    PENDING,\n    EXECUTING,\n    COMPLETED,\n    REQUIRES_REVIEW,\n    FAILED\n}\n```\n\nBefore executing a mutation:\n\n```\nAgentIntent intent = intentRepository.findById(intentId)\n    .orElseThrow();\n\nif (intent.status() == IntentStatus.COMPLETED) {\n    return previousResult(intent.resultId());\n}\n```\n\nThe exact implementation will vary.\n\nThe principle is what matters:\n\n**A retry should refer to the same business intent instead of silently becoming a new action.**\n\nImagine an account-closing agent.\n\nIt successfully executes:\n\n```\n✓ Cancel subscription\n✓ Revoke API credentials\n✓ Delete files\n✓ Generate final invoice\n✓ Close account\n```\n\nEvery API returns success.\n\nBut company policy requires:\n\n```\nExport compliance archive\nBEFORE\nDelete files\n```\n\nThe agent skipped the archive.\n\nFive green tool calls.\n\nOne invalid business process.\n\nThis is why agent observability cannot stop at:\n\n```\nTool call succeeded\n```\n\nWe need to ask:\n\n```\nWas the workflow itself valid?\n```\n\nFor read-only operations, directly exposing tools may be reasonable.\n\nFor high-impact actions, I prefer an architecture like:\n\n```\nUser Goal\n   ↓\nAI Agent\n   ↓\nIntent + Policy Gate\n   ↓\nTool Gateway\n   ↓\nBusiness API\n   ↓\nOutcome Verification\n```\n\nBefore issuing a refund, deterministic code can verify:\n\n```\ntransaction belongs to authenticated user\nAND transaction is refundable\nAND amount <= remaining refundable amount\nAND approval threshold is satisfied\nAND intent has not already completed\n```\n\nThe model proposes.\n\nThe system verifies.\n\nThe API executes.\n\nThe system verifies again.\n\nThat separation is important.\n\nSecurity controls still matter enormously.\n\nBut authorisation alone does not solve every agent problem.\n\nSuppose an agent legitimately has permission to call:\n\n```\nrestartProductionService()\n```\n\nThe credentials are valid.\n\nThe operator has the correct role.\n\nBut should the service restart now?\n\nPerhaps:\n\n```\na deployment is currently running\n```\n\nor:\n\n```\nan incident is already active\ntraffic is at its daily peak\nanother restart happened 30 seconds ago\n```\n\nAuthentication answers:\n\nWho are you?\n\nAuthorisation answers:\n\nAre you allowed to perform this operation?\n\nAgentic systems also need:\n\n**Is this action appropriate in the current context?**\n\nThat requires policy, state, and sometimes human judgement.\n\nMany tools are described approximately like this:\n\n```\nname: issue_refund\ndescription: Refund a customer transaction\n```\n\nThat tells the model what the tool does.\n\nIt does not define the conditions that make using it safe.\n\nA stronger contract might be:\n\n```\ntool: issue_refund\n\npreconditions:\n  - transaction belongs to authenticated customer\n  - transaction is refundable\n  - amount <= remaining refundable balance\n\nexecution:\n  idempotency_required: true\n\npostconditions:\n  - refund record exists\n  - refund references expected transaction\n  - refund amount matches approved amount\n  - ledger state reconciles\n\napproval:\n  required_above: 5000\n```\n\nNow success is not simply:\n\n```\nfunction returned successfully\n```\n\nIt becomes:\n\n```\npreconditions satisfied\n+\naction executed\n+\npostconditions verified\n```\n\nA tempting pattern is:\n\n```\nAgent performs action\n↓\nAgent asks itself:\n\"Did that work?\"\n↓\nAgent continues\n```\n\nFor low-risk workflows, this may be sufficient.\n\nFor important actions, it is fragile.\n\nIf the model misunderstood the original request, asking the same model whether its interpretation was correct may reproduce the same mistake.\n\nHigh-impact actions should be verified against external evidence:\n\n```\ndatabase state\npayment receipt\nledger state\npolicy engine\nindependent validator\nsensor state\nhuman approval\n```\n\nThe model can reason about these signals.\n\nIt should not invent them.\n\nImagine this dashboard:\n\n```\nrefund-api availability:       99.99%\ntool-call success rate:        98.9%\naverage API latency:           310 ms\n```\n\nEverything appears healthy.\n\nBut you are not measuring:\n\n```\nwrong-target actions\nduplicate mutations\npolicy violations\nunverified outcomes\nhuman reversals\n```\n\nAgentic systems need higher-level metrics.\n\n```\nverified_outcome_rate\nduplicate_action_rate\npostcondition_failure_rate\nhuman_override_rate\nambiguous_outcome_rate\nintent_reconciliation_rate\n```\n\nA meaningful future SLO might look like:\n\n**99.95% of high-impact agent intents complete with a verified business outcome and no duplicate side effect.**\n\nThat tells us far more than API availability.\n\nSuppose a user tells an AI commerce agent:\n\n“Cancel my duplicate order and refund it.”\n\nInstead of immediately executing actions, the workflow could be:\n\n```\n1. Create intent\n   CANCEL_DUPLICATE_ORDER_9821\n\n2. Retrieve candidate orders\n\n3. Deterministically verify:\n   - same customer\n   - duplicate item\n   - matching amount\n   - cancellable state\n\n4. Generate action preview:\n   Cancel Order A1842\n   Refund £74.99\n\n5. Request human confirmation if required\n\n6. Cancel order using intent ID\n\n7. Issue refund using same intent context\n\n8. Verify:\n   order == CANCELLED\n   refund == CONFIRMED\n   refund amount == £74.99\n\n9. Mark intent COMPLETED\n\n10. Tell user:\n    \"Done\"\n```\n\nStep 10 is the important part.\n\nThe agent does not say “done” because it received `200 OK`.\n\nIt says “done” because the system verified the intended business outcome.\n\nFor any agent capable of moving money, modifying production systems, changing permissions, deleting information, or performing irreversible actions:\n\nPersist the actual business objective.\n\nExpose only capabilities required for the task.\n\nKeep critical business rules outside the model.\n\nDesign retries so repeating a request does not repeat the side effect.\n\nVerify the resulting state using trusted systems.\n\nConnect:\n\n```\nuser intent\n→ agent decision\n→ policy result\n→ tool call\n→ API response\n→ verified business state\n```\n\nThat gives us:\n\n```\nIntent\n  ↓\nPolicy\n  ↓\nAction\n  ↓\nReceipt\n  ↓\nVerification\n  ↓\nOutcome\n```\n\ninstead of:\n\n```\nPrompt\n  ↓\nTool\n  ↓\n200 OK\n  ↓\n\"Done!\"\n```\n\nTraditional APIs mainly answer:\n\n```\nWhat operation can I call?\nWhat arguments are required?\nWhat response will I receive?\n```\n\nAI-agent-facing capabilities may need to expose more:\n\n```\nWhat risk level does this action carry?\nIs the operation reversible?\nDoes it require approval?\nCan it be retried safely?\nWhat preconditions must hold?\nWhat proves successful completion?\n```\n\nThat turns the API from a simple interface into something closer to a **capability contract**.\n\nThe AI supplies flexible reasoning.\n\nThe surrounding system supplies deterministic guarantees.\n\nWe are investing enormous effort in making AI agents smarter.\n\nBetter models.\n\nMore context.\n\nMore tools.\n\nLonger workflows.\n\nGreater autonomy.\n\nBut once an agent can modify the real world, the hardest production question may not be:\n\n**Can the model determine what to do?**\n\nIt may be:\n\n**How do we prove that what it just did was actually what the user intended?**\n\nThe most dangerous failure may never generate an exception.\n\nIt may not trigger PagerDuty.\n\nIt may not appear in the error logs.\n\nEvery service may remain healthy.\n\nAll you may see is:\n\n```\nHTTP/1.1 200 OK\n```\n\nThe AI agent completed its task.\n\nAnd the business still lost.\n\nThat is why production Agentic AI needs more than successful tool calls.\n\nIt needs **verified outcomes**.", "url": "https://wpnews.pro/news/your-api-returned-200-ok-your-ai-agent-still-failed", "canonical_source": "https://dev.to/sudhanshu_thakur_/your-api-returned-200-ok-your-ai-agent-still-failed-147g", "published_at": "2026-09-20 10:52:01+00:00", "updated_at": "2026-09-20 11:24:30.764529+00:00", "lang": "en", "topics": ["ai-agents", "ai-safety", "ai-tools", "developer-tools"], "entities": [], "alternates": {"html": "https://wpnews.pro/news/your-api-returned-200-ok-your-ai-agent-still-failed", "markdown": "https://wpnews.pro/news/your-api-returned-200-ok-your-ai-agent-still-failed.md", "text": "https://wpnews.pro/news/your-api-returned-200-ok-your-ai-agent-still-failed.txt", "jsonld": "https://wpnews.pro/news/your-api-returned-200-ok-your-ai-agent-still-failed.jsonld"}}