# Push API Tutorial
source: https://developer.mastercard.com/ethoca-alerts-for-merchants/documentation/tutorials-and-guides/push-api-tutorial/index.md

## Receive and Process Alerts with the Ethoca Alerts for Merchants Push API {#receive-and-process-alerts-with-the-ethoca-alerts-for-merchants-push-api}

This tutorial demonstrates how to receive alerts from Ethoca via webhook, acknowledge receipt, and prepare for outcome submission.

*** ** * ** ***

## Overview {#overview}

The Push API workflow consists of three steps:

1. **Register Webhook Endpoint** --- Provide your HTTPS endpoint to Ethoca
2. **Receive and Acknowledge Alerts** --- Ethoca POSTs alerts to your endpoint; you respond immediately
3. **Submit Outcomes** --- Asynchronously investigate and report results via Outcome API

This tutorial covers steps 1 and 2. See [Outcome API Tutorial](https://static.developer.mastercard.com/content/ethoca-alerts-for-merchants/documentation/tutorials-and-guides/push-api-tutorial/outcome-api-tutorial.md) for step 3.

*** ** * ** ***

## Step 1: Register Your Webhook Endpoint {#step-1-register-your-webhook-endpoint}

### Endpoint Requirements {#endpoint-requirements}

Your webhook endpoint must:

* Be publicly accessible via HTTPS (no HTTP)
* Accept POST requests
* Return HTTP 200 OK within 30 seconds
* Include acknowledgement information in the response body

### Registering with Ethoca {#registering-with-ethoca}

Provide your webhook URL to your Ethoca Customer Delivery Team during onboarding:

**Example webhook URL:** `https://yourdomain.com/ethoca/api/push/alerts/webhook`

Ethoca will:

1. Validate the URL is reachable
2. Register it in their system
3. Begin delivering alerts in real-time

*** ** * ** ***

## Step 2: Receive Webhook Alerts {#step-2-receive-webhook-alerts}

### Webhook POST Request {#webhook-post-request}

When an alert occurs, Ethoca sends a POST request to your webhook:

```bash
POST https://yourdomain.com/ethoca/api/push/alerts/webhook HTTP/1.1
Host: yourdomain.com
Content-Type: application/json
Authorization: Bearer <optional-signature-token>

{
  "alertId": "ALERT_ID_001",
  "alertType": "CUSTOMERDISPUTE",
  "transactionAmount": "150.00",
  "transactionCurrency": "USD",
  "dateSubmitted": "2024-06-23T14:30:00Z",
  "customerName": "Jane Smith",
  "cardLastFour": "5678",
  "merchantName": "Example Merchant",
  "transactionRef": "TXN_REF_001",
  "transactionDate": "2024-06-20",
  "acquirerReference": "ACQ_REF_001"
}
```

#### Webhook Payload Fields {#webhook-payload-fields}

|         Field         |  Type  |                            Description                             |
|-----------------------|--------|--------------------------------------------------------------------|
| `alertId`             | String | Unique identifier for this alert (use for acknowledge and outcome) |
| `alertType`           | String | Either `CUSTOMERDISPUTE` or `CONFIRMEDFRAUD`                       |
| `transactionAmount`   | String | Amount in decimal format (for example, "150.00")                   |
| `transactionCurrency` | String | ISO 4217 currency code (for example, "USD")                        |
| `dateSubmitted`       | String | ISO 8601 timestamp when alert was submitted                        |
| `customerName`        | String | Name of customer                                                   |
| `cardLastFour`        | String | Last four digits of card                                           |
| `merchantName`        | String | Your merchant name                                                 |
| `transactionRef`      | String | Your transaction reference                                         |
| `transactionDate`     | String | Date of transaction (YYYY-MM-DD)                                   |
| `acquirerReference`   | String | Card issuer reference                                              |

*** ** * ** ***

## Step 3: Implement Your Webhook Handler {#step-3-implement-your-webhook-handler}

### Example: Node.js/Express {#example-nodejsexpress}

```javascript
const express = require('express');
const app = express();
app.use(express.json());

app.post('/ethoca/api/push/alerts/webhook', (req, res) => {
  const alert = req.body;
  const alertId = alert.alertId;
  const alertType = alert.alertType;
  
  // Log receipt (for auditing)
  console.log(`Received alert ${alertId} of type ${alertType}`);
  
  // Queue for asynchronous processing
  processAlertAsync(alert);
  
  // Respond immediately with acknowledgement
  res.json({
    status: "SUCCESS",
    alertId: alertId,
    processedAt: new Date().toISOString()
  });
});

// Asynchronous processing (investigate, submit outcome)
function processAlertAsync(alert) {
  setImmediate(() => {
    investigateAndSubmitOutcome(alert);
  });
}

app.listen(3000, () => {
  console.log('Webhook listener running on port 3000');
});
```

### Example: Java/Spring Boot {#example-javaspring-boot}

```java
@RestController
@RequestMapping("/ethoca/api/push/alerts")
public class AlertWebhookController {
  
  @PostMapping("/webhook")
  public ResponseEntity<WebhookAcknowledgement> receiveAlert(@RequestBody Alert alert) {
    String alertId = alert.getAlertId();
    String alertType = alert.getAlertType();
    
    // Log receipt
    logger.info("Received alert {} of type {}", alertId, alertType);
    
    // Queue for asynchronous processing
    alertProcessor.processAsync(alert);
    
    // Respond immediately
    WebhookAcknowledgement ack = new WebhookAcknowledgement();
    ack.setStatus("SUCCESS");
    ack.setAlertId(alertId);
    ack.setProcessedAt(LocalDateTime.now());
    
    return ResponseEntity.ok(ack);
  }
}
```

### Example: Python/Flask {#example-pythonflask}

```python
from flask import Flask, request, jsonify
import threading
from datetime import datetime

app = Flask(__name__)

@app.route('/ethoca/api/push/alerts/webhook', methods=['POST'])
def receive_alert():
    alert = request.json
    alert_id = alert['alertId']
    alert_type = alert['alertType']
    
    # Log receipt
    print(f"Received alert {alert_id} of type {alert_type}")
    
    # Queue for asynchronous processing
    thread = threading.Thread(target=process_alert_async, args=(alert,))
    thread.daemon = True
    thread.start()
    
    # Respond immediately
    return jsonify({
        "status": "SUCCESS",
        "alertId": alert_id,
        "processedAt": datetime.now().isoformat()
    })

def process_alert_async(alert):
    investigate_and_submit_outcome(alert)
```

*** ** * ** ***

## Step 4: Webhook Response Format {#step-4-webhook-response-format}

Your webhook **must** return HTTP 200 OK with a JSON body containing acknowledgement:

```json
{
  "status": "SUCCESS",
  "alertId": "ALERT_ID_001",
  "processedAt": "2024-06-23T14:30:05Z"
}
```

*** ** * ** ***

## Step 5: Handle Webhook Retries {#step-5-handle-webhook-retries}

Ethoca retries failed webhook deliveries using exponential backoff:

* **Attempt 1:** Immediate
* **Attempt 2:** 60 seconds later
* **Attempt 3:** 300 seconds later (5 minutes)
* **Attempt 4:** 900 seconds later (15 minutes)

**Your endpoint must be idempotent** --- if you receive the same alert twice, process it safely without duplication:

```java
// Store received alert IDs in a deduplication cache
Set<String> recentAlertIds = cache.get("recent_alerts");

if (recentAlertIds.contains(alert.getAlertId())) {
  // Already processed; still respond with SUCCESS
  logger.info("Duplicate alert received; skipping processing");
  return ResponseEntity.ok(acknowledgement);
}

// New alert; process it
recentAlertIds.add(alert.getAlertId());
processAlert(alert);
```

*** ** * ** ***

## Step 6: Investigate and Submit Outcomes {#step-6-investigate-and-submit-outcomes}

After acknowledging the webhook, investigate the transaction asynchronously. You have **up to 24 hours** to submit investigation outcomes.

```java
// After webhook acknowledgement is returned
@Async
public void processAlertAsync(Alert alert) {
  // Investigate the transaction
  InvestigationResult result = investigateTransaction(
    alert.getTransactionRef(),
    alert.getCardLastFour(),
    alert.getTransactionAmount()
  );
  
  // Submit outcome (see Outcome API Tutorial)
  submitOutcome(alert.getAlertId(), alert.getAlertType(), result);
}
```

See [Outcome API Tutorial](https://static.developer.mastercard.com/content/ethoca-alerts-for-merchants/documentation/tutorials-and-guides/push-api-tutorial/outcome-api-tutorial.md) for detailed outcome submission examples.

*** ** * ** ***

## Common Patterns {#common-patterns}

### Webhook Signature Validation {#webhook-signature-validation}

If signature validation is enabled, Ethoca includes a signature header. Validate it:

```java
// Extract signature from request headers
String signature = request.getHeader("X-Ethoca-Signature");

// Compute expected signature
String payload = readRequestBody(request);
String expected = computeHmacSha256(payload, YOUR_WEBHOOK_SECRET);

if (!signature.equals(expected)) {
  logger.error("Invalid signature");
  return ResponseEntity.status(401).build();
}
```

### Monitoring Webhook Health {#monitoring-webhook-health}

Track webhook delivery and response times:

```java
@PostMapping("/ethoca/alerts/webhook")
public ResponseEntity<WebhookAcknowledgement> receiveAlert(@RequestBody Alert alert) {
  long startTime = System.currentTimeMillis();
  
  try {
    // Process webhook
    WebhookAcknowledgement ack = processWebhook(alert);
    
    // Record success
    long duration = System.currentTimeMillis() - startTime;
    metrics.recordWebhookSuccess(alert.getAlertType(), duration);
    
    return ResponseEntity.ok(ack);
  } catch (Exception e) {
    metrics.recordWebhookFailure(alert.getAlertType(), e);
    throw e;
  }
}
```

### Dead Letter Queue {#dead-letter-queue}

For alerts that fail processing after retries:

```java
@Async
public void processAlertAsync(Alert alert) {
  int maxRetries = 3;
  int retryCount = 0;
  
  while (retryCount < maxRetries) {
    try {
      submitOutcome(alert);
      return; // Success
    } catch (Exception e) {
      retryCount++;
      if (retryCount >= maxRetries) {
        // Send to dead letter queue for manual review
        deadLetterQueue.send(alert);
        logger.error("Alert {} sent to DLQ after {} retries", 
          alert.getAlertId(), maxRetries);
      }
    }
  }
}
```

*** ** * ** ***

## Webhook Testing {#webhook-testing}

Test your webhook locally:

```bash
# Test webhook locally with curl
curl --request POST \
  --url http://localhost:3000/ethoca/alerts/webhook \
  --header 'Content-Type: application/json' \
  --data '{
    "alertId": "TEST_ALERT_001",
    "alertType": "CUSTOMERDISPUTE",
    "transactionAmount": "100.00",
    "transactionCurrency": "USD",
    "dateSubmitted": "2024-06-23T14:30:00Z",
    "customerName": "Test User",
    "cardLastFour": "5678",
    "merchantName": "Test Merchant",
    "transactionRef": "TEST_TXN_001",
    "transactionDate": "2024-06-20",
    "acquirerReference": "TEST_ACQ_001"
  }'
```

Expected response:

```json
{
  "status": "SUCCESS",
  "alertId": "TEST_ALERT_001",
  "processedAt": "2024-06-23T14:30:05Z"
}
```

*** ** * ** ***

## Next Steps {#next-steps}

* Review [Outcome API Tutorial](https://static.developer.mastercard.com/content/ethoca-alerts-for-merchants/documentation/tutorials-and-guides/push-api-tutorial/outcome-api-tutorial.md) to submit investigation results
* See [Testing](https://developer.mastercard.com/ethoca-alerts-for-merchants/documentation/tutorials-and-guides/testing/index.md) for comprehensive test scenarios
* Check [Codes \& Formats](https://developer.mastercard.com/ethoca-alerts-for-merchants/documentation/tutorials-and-guides/code-and-formats/index.md) for error code reference
* Visit [API Reference](https://developer.mastercard.com/ethoca-alerts-for-merchants/documentation/tutorials-and-guides/api-reference/index.md) for complete endpoint documentation
