/blue Endpoint
The /blue
endpoint adds programmable document capabilities to any API with minimal integration effort. By adding this standardized endpoint to your service, you enable clients to submit Blue documents that can interact with your core services while adding sophisticated rules, verification, and multi-party capabilities.
Most importantly, by allowing your customers to represent your services as Blue documents, you give them the opportunity to embed these services into other documents they use, enabling integration with all kinds of tools. You also become a party to which trust can be delegated, according to Blue's philosophy. All of this results in possible additional revenue streams and deeper customer relationships.
Why Add a /blue
Endpoint?
Adding Blue document support to your API creates significant business opportunities without requiring changes to your core functionality:
- Handle "impossible" edge cases: Support complex scenarios without custom development
- Enable multi-party verification: Create transparent processes visible to all participants
- Add new capabilities instantly: Respond to market needs without lengthy development cycles
- Maintain backward compatibility: Your existing API remains unchanged
- Join the interoperability network: Connect with other Blue-enabled services
Integration Strategy Options
There are several approaches to integrating the /blue
endpoint with your existing services, depending on your needs and technical capabilities:
Level 1: MyOS-Powered Integration
- Use MyOS to handle all document processing
- Your service simply forwards documents and receives webhooks
- Simplest implementation with minimal development effort
- Best for initial deployments
Level 2: Verification-Based Integration
- Receive updates from MyOS, including the triggering events
- Run your own Blue processor to verify the changes
- Confirm that the original event actually results in the reported change
- Better security model while leveraging MyOS infrastructure
Level 3: Independent Integration
- Use your own timeline implementation
- Don't rely on MyOS at all
- Complete control over the entire processing pipeline
- Highest autonomy but more complex implementation
In this guide, we'll demonstrate the simplest approach (Level 1) where MyOS handles all processing. This example uses a Stripe-like payment processor to illustrate the integration.
Minimal Implementation
Here's a minimal implementation for a Stripe-like payment processor using the spring-blue-gateway-starter
:
/* ──────────────────────────────────────────────────────────────── */
/* 0. Declare what this gateway supports */
/* ──────────────────────────────────────────────────────────────── */
@BlueGateway(
rootTypes = { Payment.class, PaymentPlan.class },
capabilityPacks = { MinimalContractPack.class },
languagePacks = { FinancialLanguagePack.class,
CommonEntitiesPack.class },
envPreset = MyOsDefault.class // ← single preset we allow
)
class GatewayCfg { }
The @BlueGateway
annotation configures what your endpoint supports:
rootTypes
: The document types your service can process (in this case, payment-related documents)capabilityPacks
: Sets of contracts and operations your service understandslanguagePacks
: Pre-defined types your service can work withenvPreset
: The processing environment you support
While you can support multiple processing environments, in this example we've chosen to support only the MyOS environment. The environment setting controls important aspects like:
- Which timeline implementations are accepted
- What coordination mechanisms are available
- How cross-document references are resolved
- Where processing actually occurs
/* ──────────────────────────────────────────────────────────────── */
/* 1. REST controller – one POST, one optional GET */
/* ──────────────────────────────────────────────────────────────── */
@RestController
@RequiredArgsConstructor
class BlueEndpoint {
private final Blue blue; // parser + helpers from starter
private final CapabilityChecker caps; // bean that knows your packs
private final MyOsClient myos; // wraps POST /agents
private final PaymentRouter router; // routes payment documents
/* ---- (optional) capabilities discovery ------------------------ */
@GetMapping(value = "/blue/capabilities", produces = "application/json")
public String caps() {
return caps.asJson(); // auto-generated manifest
}
/* ---- process document and create agent/payment as needed ------ */
@PostMapping(value = "/blue",
consumes = MediaType.APPLICATION_JSON_VALUE,
produces = MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<String> launch(@RequestBody String rawJson) {
// 1) Parse JSON into Blue Node
Node doc = blue.jsonToNode(rawJson);
// 2) Validate root + contract types against declared capability packs
caps.assertAccepted(doc); // throws 422 if anything unsupported
// 3) Process based on document type
String paymentId = null;
Node updatedDoc = null;
if (blue.isNodeSubtypeOf(doc, ClassicCardPayment.class)) {
// Process Classic Payment directly through payment processor only
paymentId = router.processPayment(doc);
// Update the document with payment ID
ObjectNode objectDoc = (ObjectNode) doc;
objectDoc.put("paymentProcessorId", paymentId);
// For Classic payments, we don't create a MyOS agent
updatedDoc = objectDoc;
}
else if (blue.isNodeSubtypeOf(doc, SmartCardPayment.class)) {
// Process Smart Payment through both payment processor and MyOS
// Create payment in processor system first
paymentId = router.processPayment(doc);
// Add the payment processor ID to the document
ObjectNode objectDoc = (ObjectNode) doc;
objectDoc.put("paymentProcessorId", paymentId);
// Create agent in MyOS with updated document
MyOSResponse response = myos.createAgent(blue.nodeToJson(objectDoc));
// MyOS returns the full document with timeline IDs added
updatedDoc = blue.jsonToNode(response.getDocument());
}
else {
// For Payment Plan or other documents, just create an agent in MyOS
MyOSResponse response = myos.createAgent(rawJson);
// Get the updated document from MyOS
updatedDoc = blue.jsonToNode(response.getDocument());
}
// Return the full updated document
return ResponseEntity.ok(blue.nodeToJson(updatedDoc));
}
}
// Simplified MyOS response model
class MyOSResponse {
private String agentId;
private String document; // JSON string of updated document
public String getAgentId() {
return agentId;
}
public String getDocument() {
return document;
}
}
The /blue/capabilities
endpoint is crucial as it allows clients to discover what document types and contracts your service supports. This enables:
- Automatic document validation before submission
- Dynamic construction of compatible documents
- Discovery of your service's capabilities by tools and other services
Runtime Flow
- Client POSTs a document to
/blue
- Your endpoint validates it against supported capabilities
- For classic payments, process directly through your core services
- For smart payments, forward to MyOS for processing
- Return the appropriate ID and BlueId
- Future updates arrive via webhook
Handling Webhooks
The webhook handler translates document events into operations on your core services:
/* ──────────────────────────────────────────────────────────────── */
/* 1. Event classes (from blue.repo) */
/* ──────────────────────────────────────────────────────────────── */
@TypeBlueId("E7aQ…")
public record RefundPayment(String paymentId, long amount) {}
@TypeBlueId("F2bK…")
public record MakeInternalStripePayment(
long amount, String recipientStripeAccount, String notes) {}
/* ──────────────────────────────────────────────────────────────── */
/* 2. Business service */
/* ──────────────────────────────────────────────────────────────── */
@Service
class PaymentProcessor {
void refund(String paymentId, long amount) {
// call internal ledger or external Stripe API
}
void capture(long amount, String recipient, String notes) {
// call internal ledger or external Stripe API
}
}
/* ──────────────────────────────────────────────────────────────── */
/* 3. Webhook payload DTO */
/* ──────────────────────────────────────────────────────────────── */
record MyOsWebhook(
String agentId,
String document, // raw JSON string
List<String> triggeredEvents) {} // each raw event JSON
/* ──────────────────────────────────────────────────────────────── */
/* 4. Webhook endpoint */
/* ──────────────────────────────────────────────────────────────── */
@RestController
@RequiredArgsConstructor
class MyOsWebhookEndpoint {
private final Blue blue; // helper from starter
private final SnapshotStore store; // keeps latest doc
private final PaymentProcessor payments;
private final Logger logger = LoggerFactory.getLogger(MyOsWebhookEndpoint.class);
@PostMapping(value="/blue/webhook",
consumes=MediaType.APPLICATION_JSON_VALUE)
public void handle(@RequestBody MyOsWebhook wh) {
/* 1 ─ parse and save the latest document */
Node doc = blue.jsonToNode(wh.document());
store.upsertLatest(wh.agentId(), doc);
/* 2 ─ inspect triggered events */
for (String raw : wh.triggeredEvents()) {
Node evNode = blue.jsonToNode(raw);
// Determine the event class based on its BlueId
Optional<Class<?>> eventClass = blue.determineClass(evNode);
if (eventClass.isPresent()) {
Class<?> clazz = eventClass.get();
// Process known event types
if (RefundPayment.class.isAssignableFrom(clazz)) {
RefundPayment ev = blue.nodeToObject(evNode, RefundPayment.class);
payments.refund(ev.paymentId(), ev.amount());
logger.info("Processed refund for payment {}: ${}", ev.paymentId(), ev.amount());
}
else if (MakeInternalStripePayment.class.isAssignableFrom(clazz)) {
MakeInternalStripePayment ev = blue.nodeToObject(evNode, MakeInternalStripePayment.class);
payments.capture(ev.amount(), ev.recipientStripeAccount(), ev.notes());
logger.info("Processed capture: ${} for account {}",
ev.amount(), ev.recipientStripeAccount());
}
// Add handlers for other payment-related events as needed
}
}
}
}
When a document triggers events like MakeInternalStripePayment
, your webhook handler calls the appropriate method on your payment service, integrating Blue documents with your existing business logic.
Supported Document Types
As a payment processor, you might support these Blue document types:
Card Payment
A standard payment transaction with enhanced capabilities:
See Card Payment for detailed examples, including guaranteed delivery discounts and cryptocurrency exchange with protection.
Payment Plan
A dynamic document that manages when and how payments are triggered:
See Payment Plan for examples of subscription and event-based payment scenarios.
Security and Authentication
The /blue
endpoint integrates with your existing authentication:
@PostMapping(value = "/blue")
@PreAuthorize("hasAuthority('PAYMENT_PROCESSING')")
public ResponseEntity<?> securedBlueEndpoint(@RequestBody String rawJson) {
// Process document with normal auth checks
}
This ensures that:
- Your security policies apply to Blue documents
- Access control uses your existing mechanisms
- Audit trails maintain continuity with your current systems
Benefits for All Participants
A payment processor implementing the /blue
endpoint creates value for all parties:
For Merchants:
- Handle complex payment scenarios without custom code
- Create better customer experiences with transparent terms
- Add payment rules that integrate with other business processes
For Customers:
- Verify that payments only happen under agreed conditions
- Gain visibility into payment status and logic
- Reduce risk in complex transactions
For the Payment Processor:
- Differentiate with advanced payment capabilities
- Expand into new use cases beyond standard payments
- Add value without changing core payment systems
Implementation Steps
-
Add Dependencies:
implementation 'blue.starter:spring-blue-gateway-starter:1.0.0'
implementation 'blue.repo:payments:1.12' -
Configure Your Gateway: Define which document types and contracts your endpoint supports
-
Create Endpoint and Webhook Handler: Add the controllers shown in this guide
-
Connect to Your Core Services: Map document events to your business logic
-
Test with Sample Documents: Verify processing of basic and advanced payment scenarios