Skip to content

Best Practices ​

The SDK is small and well-typed; using it safely is more about how you wire it into your application than about the SDK itself. These are the patterns we've seen work in production.

Credentials ​

Never commit keys to source

Use .env (gitignored), environment variables, or a secrets manager. The SDK reads plain strings — it doesn't care where they came from.

One key per service

If service A leaks its key, you revoke and reissue without touching service B. Keys are cheap; rotation should be local.

Name keys for traceability

checkout-api-prod beats key3. The name appears in audit logs and the API Keys page.

Rotate periodically

Even without leaks. Quarterly is sensible. Tie it to your existing secrets-rotation cadence.

Construct once, use many ​

The constructor is cheap but adds zero value when called multiple times. Single instance per process, injected as a dependency:

python
# app/deps.py
import os
from dotenv import load_dotenv
from elsai_prompts.prompt_manager import PromptManager

load_dotenv()

pm = PromptManager(
    api_key=os.environ["ELSAI_API_KEY"],
    project_id=os.environ["ELSAI_PROJECT_ID"],
    environment=os.environ["ELSAI_ENVIRONMENT"],
)
python
# Anywhere else:
from app.deps import pm
prompt = pm.get_active_prompt_version("...")

Cache fetches ​

The SDK does no caching. Every get_active_prompt_version call is a network round trip. That's deliberate — caching policy is application-specific.

Simple in-memory cache (good default):

python
from datetime import datetime, timedelta, timezone

_cache = {}
_TTL = timedelta(seconds=60)

def get_prompt(name):
    now = datetime.now(timezone.utc)
    hit = _cache.get(name)
    if hit and now - hit[0] < _TTL:
        return hit[1]
    prompt = pm.get_active_prompt_version(name)
    _cache[name] = (now, prompt)
    return prompt

For multi-process apps, swap the dict for Redis with the same shape.

TIP

TTL tradeoff: shorter = fresher prompts, more API load; longer = staler prompts, more resilience to upstream outages. 60 seconds is a defensible default.

Add observability ​

Wrap fetches with metrics + structured logs:

python
import time
import logging

logger = logging.getLogger(__name__)

def fetch_observed(name):
    started = time.monotonic()
    try:
        prompt = pm.get_active_prompt_version(name)
        elapsed_ms = (time.monotonic() - started) * 1000
        logger.info(
            "prompt_fetch_ok",
            extra={"prompt_name": name, "sha": prompt.sha, "ms": elapsed_ms},
        )
        return prompt
    except Exception as e:
        elapsed_ms = (time.monotonic() - started) * 1000
        logger.exception(
            "prompt_fetch_failed",
            extra={"prompt_name": name, "ms": elapsed_ms, "error": type(e).__name__},
        )
        raise

With OpenTelemetry:

python
from opentelemetry import trace
tracer = trace.get_tracer(__name__)

def fetch_traced(name):
    with tracer.start_as_current_span("prompt.fetch", attributes={"prompt.name": name}) as span:
        prompt = pm.get_active_prompt_version(name)
        span.set_attribute("prompt.sha", prompt.sha or "")
        return prompt

Handle 409 explicitly ​

The 409 path means "the platform is refusing to serve unreleased content." How you respond depends on whether the prompt is critical:

python
from elsai_prompts.prompt_manager import PromptNotReleasedInEnvironmentError

# Critical path - fail loudly:
try:
    prompt = pm.get_active_prompt_version("payment_confirmation")
except PromptNotReleasedInEnvironmentError:
    logger.critical("payment_confirmation prompt missing in production")
    raise   # page the on-call

# Non-critical - fall back:
try:
    prompt = pm.get_active_prompt_version("footer_message")
    text = prompt.render({})
except PromptNotReleasedInEnvironmentError:
    text = "© 2026 Acme Inc."   # safe default

Never except Exception away a 409 — you'll silently mask configuration bugs.

Pre-warm on startup ​

For a hot path that can't afford a cold-cache miss, fetch the prompt at startup:

python
PROMPTS_TO_PREWARM = ["welcome_email", "support_dialog", "summarizer"]

def prewarm_prompts():
    for name in PROMPTS_TO_PREWARM:
        try:
            _ = pm.get_active_prompt_version(name)
            logger.info("prewarmed %s", name)
        except Exception as e:
            logger.exception("prewarm failed for %s", name)

If you fail-fast on prewarm errors, your deploy will refuse to roll out when a critical prompt is misconfigured — that's usually the right tradeoff for production.

Lock the SDK version ​

Pin elsai-prompts to a specific version in your requirements file:

elsai-prompts==2.0.0   # SaaS
elsai-prompts==2.0.1   # on-prem

Not >=2.0.0. Not ~=2.0. A specific version. SDK upgrades should be a deliberate change with a tested release, not something that comes along on pip install.

Test against the real platform ​

Mocking the SDK in unit tests is fine for application logic. But have at least one integration test that hits a real prompt against a test project:

python
def test_integration_fetches_real_prompt():
    """Hits the real platform - requires test creds in env."""
    pm = PromptManager(
        api_key=os.environ["ELSAI_TEST_KEY"],
        project_id=os.environ["ELSAI_TEST_PROJECT"],
        environment="development",
    )
    prompt = pm.get_active_prompt_version("smoke_test_prompt")
    assert prompt.kind in ("instruction", "f_string", "chat", "structured")

This catches schema drift between the SDK and the platform that mocked tests never will.

Don't reinvent variable validation ​

Use prompt.variables as the source of truth:

python
def render_safe(prompt, inputs):
    declared = {v["name"] for v in prompt.variables}
    extra = inputs.keys() - declared
    missing = declared - inputs.keys()
    if missing:
        raise ValueError(f"Missing: {missing}")
    if extra:
        logger.warning("Extra inputs ignored: %s", extra)
    return prompt.render(inputs)

Common anti-patterns ​

❌ Constructing a new PromptManager per request

No benefit, plus you re-parse env vars on every request. Construct once at startup.

❌ Catching all exceptions and returning a default

Masks 409s (release config bugs), 404s (key revoked, prompt renamed), and 5xx (platform down). Each deserves a different response. Match exception types explicitly.

❌ Hardcoding prompts as a fallback for the same content

Defeats the point of centralized prompts. If you must have a default, make it a safe degraded message, not the same content the prompt would have produced.

❌ Using environment='production' in development

Causes 409s if the production version differs from dev, and worse, lets dev code silently serve customer-facing prompts. Use the environment that matches your runtime.

❌ Skipping the env filter ('just give me whatever is active')

The env filter is the whole point — it prevents unreleased content from leaking. There's no SDK flag to disable it, and there shouldn't be.

Copyright © 2026 elsai foundry.