0Pricing
AI Agents · Lesson

Idempotent Tools and Side Effects

An agent may retry — design tools so calling them twice doesn't double-charge or double-email.

Idempotent Tools and Side Effects is a free AI Agents lesson on CoddyKit — lesson 1 of 4. You can read the complete lesson below for free — then practise it hands-on in the browser with a built-in code editor and a 24/7 AI tutor. It is part of the AI Agents learning path, one of 4 lessons in the course, and your progress syncs across the web and the CoddyKit app.

Why Idempotency Matters

An agent loop may retry. If a tool sends an email or charges a credit card, retrying duplicates the action. Idempotency makes "do X again" a no-op when X already happened.

What Is Idempotency?

An operation is idempotent if calling it N times produces the same effect as calling it once. Examples:

  • "Set user status to active" — idempotent (re-setting to active does nothing)
  • "Increment counter by 1" — NOT idempotent (each call adds 1 more)

Idempotency Keys

For non-idempotent operations (payments, emails), use idempotency keys:

import uuid

def create_payment(amount, idempotency_key):
    # Stripe accepts this header — returns the existing payment if key was used
    return stripe.Charge.create(
        amount=amount,
        currency='usd',
        source=token,
        idempotency_key=idempotency_key
    )

Generate Keys From Intent

Use a hash of (tool_name, arguments, run_id) so identical retries reuse the same key:

import hashlib, json

def intent_key(tool_name, args, run_id):
    payload = json.dumps([tool_name, args, run_id], sort_keys=True)
    return hashlib.sha256(payload.encode()).hexdigest()

print(intent_key('transfer_money', {'to': 'acc2', 'amount': 100}, 'run-1'))

Side-Effect Inventory

List which tools have side effects:

  • SEND email / SMS
  • CHARGE / refund
  • CREATE / DELETE database rows
  • POST to external APIs that mutate state

Read-only tools are inherently idempotent. Focus on the writes.

Wrap Side-Effect Tools

Add an idempotency layer in the tool implementation:

def send_email(to, subject, body, run_id):
    key = intent_key('send_email', {'to': to, 'subject': subject, 'body': body}, run_id)
    if redis.set(f'sent:{key}', 1, nx=True, ex=86400):
        smtp.send(to, subject, body)
        return {'sent': True}
    return {'sent': False, 'reason': 'already-sent'}

Confirm Before Acting

For high-stakes actions, add a confirmation step:

def transfer_money(from_acc, to_acc, amount, confirmed=False):
    if not confirmed:
        return {'pending': True, 'msg': 'Call again with confirmed=True to proceed.'}
    return {'pending': False, 'msg': f'Transferred {amount} from {from_acc} to {to_acc}.'}

print(transfer_money('acc1', 'acc2', 100))
print(transfer_money('acc1', 'acc2', 100, confirmed=True))

Two-Phase Tools

Split tools into "draft" and "commit" phases:

def draft_email(to, subject, body):
    draft_id = save_draft(to, subject, body)
    return {'draft_id': draft_id, 'preview': body[:200]}

def send_draft(draft_id):
    return smtp.send(load_draft(draft_id))

Compensating Actions

If you cannot avoid a side effect, define a "undo" tool:

def refund_payment(payment_id):
    return stripe.Refund.create(payment=payment_id)
# Now an erroneous create_payment can be undone.

Audit Logs

Every side-effect tool MUST log: who triggered it, what arguments, when, the result. Use structured logs that you can query later.

Per-Run Side-Effect Budget

Cap side effects per run as a safety net:

if state.side_effect_count >= 5:
    return {'error': 'Side-effect budget exhausted for this run.'}

Dry-Run Mode

Useful during development: tools log what they WOULD do but do not actually do it:

import os
os.environ['DRY_RUN'] = '1'
DRY_RUN = os.getenv('DRY_RUN') == '1'

def send_email(to, subject, body):
    if DRY_RUN:
        print(f'would send email to {to}')
        return {'sent': False, 'dry_run': True}
    print(f'sending email to {to}')
    return {'sent': True, 'dry_run': False}

result = send_email('user@example.com', 'Hi', 'Hello there')
print(result)

Definition

What does "idempotent" mean for a tool?

Recap

Identify side effects. Use idempotency keys. Build two-phase tools and compensating actions. Audit everything. Dry-run in dev.

Frequently asked questions

Is the “Idempotent Tools and Side Effects” lesson free?

Yes — the full text of “Idempotent Tools and Side Effects” is free to read here on the web, and the AI Agents course includes 4 lessons in total. To practise it interactively (a built-in code editor and a 24/7 AI tutor) and unlock the rest of the AI Agents course, upgrade to CoddyKit PRO.

What will I learn in “Idempotent Tools and Side Effects”?

An agent may retry — design tools so calling them twice doesn't double-charge or double-email. You practise AI Agents with hands-on code you run directly in the browser, and a 24/7 AI tutor answers your questions as you work through the lesson.

Do I need any experience to start AI Agents?

No prior experience is required. AI Agents on CoddyKit is structured for beginners through advanced learners; this is — lesson 1 of 4, so you can start here or from the beginning and move at your own pace.

How long does the “Idempotent Tools and Side Effects” lesson take?

Most CoddyKit lessons take about 5–10 minutes. Each one is bite-sized and interactive, so you make steady progress and pick up exactly where you left off across the web and the app.

Can I write and run code in this AI Agents lesson?

Yes. Every AI Agents lesson includes a built-in code editor, so you write and run real code right in your browser and get instant AI feedback — no local setup required.

All lessons in this course

  1. Idempotent Tools and Side Effects
  2. Retries with Exponential Backoff
  3. Timeouts and Circuit Breakers
  4. Validating Tool Outputs (Pydantic)
← Back to AI Agents